May 27, 2015

Bringing Meteor’s reactive collections to Backbone

Bringing Meteor's Reactive Collections to Backbone | Mixmax

Last month, we migrated part of our app from Meteor to Backbone in order to improve our load time. As we wrote then, our users benefit from the performance gains provided by Backbone’s minimal footprint and support for attaching views to server-side-rendered DOM, but we found ourselves missing Meteor’s reactive front-end. Today, we are pleased to announce progress toward re-implementing such reactivity in Backbone.

In Meteor, reactivity means that:

  1. When data changes server-side, the server pushes relevant changes to clients.
  2. When client-side data changes, templates using that data automatically update.

We chose to focus on the first of these goals. Manually re-rendering templates when data changes isn’t as big a pain as not having that data at all. And with parts of our application in Meteor, it was critical to synchronize their data with the Backbone app to present the realtime UI our users have come to expect.

It would have been possible to build out a polling architecture or to use a raw websocket, but we wondered if there was a way to connect our frontend to our existing Meteor backend. Luckily, Meteor’s data protocol is open-source, and there are several JavaScript client libraries.

At first, we considered Mondora’s Asteroid project, but dismissed this as unnecessarily complex given our constraints:

  • We only need to support the browser.
  • We only need our Meteor collections to be read-only—we use AJAX to write back to the server, to be able to cancel and parallelize requests.

And, Asteroid does not support fine-grained change notifications, which we wanted for ease of integration into Backbone.

Luckily, Mondora has separately open-sourced just the part of Asteroid that speaks DDP as ddp.js. Using that project, we were able to re-implement Meteor subscriptions and reactive database queries in less than 300 LoC. Here’s what it looks like in use:

// Tells the server to start sending preferences documents to the client.
Meteor.subscribe('userpreferences');

// Reactively query the preferences of user ‘foo’.
var query = Meteor.getCollection('userpreferences').find({
  userId: 'foo'
});

// Print changes to the documents in the query’s result set.
query.on('changed', function(attrs) {
  console.log('The preferences that changed are:', attrs);
});

Using these reactive queries, it’s really easy to synchronize our Backbone collections with the Meteor collections using a Backbone.Collection subclass, Backbone.MeteorCollection. As the query changes, the contents of the Backbone collection are kept in sync, with the Meteor records automatically being converted into instances of the collection’s model and the Backbone collection emitting the usual change events.

One way in which we’re using Backbone.MeteorCollection is to immediately unlock full access to email tracking after the user upgrades to a paid plan. We render the tracking menu or an upsell depending on whether the user has an email tracking “feature”. By making the “features” collection an instance of Backbone.MeteorCollection, we can detect the feature being granted to the user as soon as the purchase completes on our server:

var FeaturesCollection = new Backbone.MeteorCollection({}, {
  model: FeatureModel,
  reactiveQuery: Meteor.getCollection('features').find({ userId: 'foo' })
});

// Load the initial data, bootstrapped into the page when it is rendered on the server:
// http://backbonejs.org/#FAQ-bootstrap
FeaturesCollection.reset(});

// Render the initial state of the UI;
if (FeaturesCollection.findWhere({ name: 'emailTracking' })) {
  renderTrackingMenu();
} else {
  renderTrackingUpsell();
}

// Begin tracking changes to the features data.
Meteor.subscribe('features');

// Update the UI when the data changes.
FeaturesCollection.on('add', function(feature) {
  if (feature.get('name') === 'emailTracking') {
    renderTrackingMenu();
  }
});

As you can see, reactive data sources integrate very gracefully into Backbone. With a little bit of polish, we’re looking forward to releasing our Meteor client and Backbone.MeteorCollection as full-fledged open-source projects. Future work might include implementing reactive templates or support for writing changes back to the server using DDP as an alternative to AJAX.

Have any particular requests for Meteor functionality in Backbone? Email careers@mixmax.com or send us a tweet @Mixmax!

You deserve a spike in replies, meetings booked, and deals won.

Try Mixmax free