A History of Data Management in Meteor

Abhi Aiyer
5 min readDec 18, 2015

--

It’s funny, the way we subscribe to data in Meteor has changed so much over time. From the beginnings with Iron Router and waitOn calls, to template level subscriptions and now with React and Angular in the mix, the confusion is even greater! We have tutorials doing this in every different flavor…and that is the PROBLEM.

What are subscriptions?

Subscriptions are a way to send data to the client via WebSockets. In Meteor this tunnel is communicated across the “wire” via DDP. DDP, the Distributed Data Protocol, is a simple protocol for fetching structured data from a server and receiving live updates when that data changes.

For us to work with data on the client, we need to subscribe to “publications”, which publishes this data over the DDP connection and dumped into a local cache/local database called Minimongo.

Let’s travel through time and let me show you all the different patterns we’ve used to achieve data flow in Meteor Apps.

waitOn, subscriptions: Iron Router — The Dark Ages

When Meteor first hit the scene, data management was up in the air. It wasn’t until Chris Mather’s package Iron Router hit the scene that Meteor developers had a clear place to not only manage Routing but subscriptions and layout rendering as well.

We at Workpop leveraged Iron Router heavily, like the many Meteor developers before us, to get our product to market. It wasn’t until our app hit a certain scale did we see the troubles with reactive routers and subscriptions in those routers.

Router.route('/post/:_id', {
// this template will be rendered until the subscriptions are ready
loadingTemplate: 'loading',

waitOn: function () {
// return one handle, a function, or an array
return Meteor.subscribe('post', this.params._id);
},

action: function () {
this.render('myTemplate');
}
});

This approach works great for small apps. Your data is relatively low. You can subscribe on the client and your view will be rendered once DDP signals to the client that the data is ready. So why is this bad?

Well, the problem with Meteor subscriptions is that they persist until stopped. And for most, it wasn’t very clear until their apps reached some scale that stopping subscriptions was necessary to prevent data overload on the client.

Aside from the lack of transparent stop functions baked into the router (seriously who remembered onStops for the routes), reactive parameters constantly would invalidate Route functions causing reruns of the route proving to be a very UNPREDICTABLE atmosphere for development.

That being said, your mileage may very. I did enjoy Iron Router when my app was smaller! And I’m sure you will too!

Blaze Template Level Subscriptions — The Silver Age

Moving past Iron Router, MDG stepped gave us a solution in Meteor v1.0.4….The Template Level Subscription. This solution is beautiful. It allows Blaze users to utilize Template instances to subscribe to data and functions exactly like Meteor.subscribe, but stops the subscription when the template is destroyed.

This also gave users a new method on Template instances called subscriptionsReady , a reactive function that returns true when all of the subscriptions made in the template come back as ready.

Template.exampleComponentContainer.onCreated(function () {
const template = this;
template.autorun(()=> {
template.subscribe(‘exampleSubscription’);
});
});
Template.exampleComponent.helpers({
exampleData() {
return ExampleData.findOne({_id: ‘1’}, {fields: {‘name’: 1}});
}
});
// HTML// Container Component
<template name=”exampleComponentContainer”>
{{#if Template.subscriptionsReady}}
{{> Template.contentBlock}}
{{else}}
{{> loading }}
{{/if}}
</template>
// Example Component
<template name=”exampleComponent”>
<h1>{{exampleData.name}}</h1>
</template>
// Page Component
<template name=”examplePage”>
{{#exampleComponentContainer}}
{{> exampleComponent}}
{{/exampleComponentContainer}}
</template>

See how nice that is? Definitely an improvement from an abstract routing layer. Now you can replace the word template with the component in your development dictionary and start approaching them as functional units of your UI.

Angular Controllers Subscriptions — The Golden Age

Starting with Meteor 1.2, users had first class support for AngularJS. This was another great burst of new methods of data fetching. The interesting thing here is that even with angular, the move to a more component data fetching model remained true. In Meteor Angular, the controller for your component is the perfect place to subscribe to data.

Angular Meteor ships with a $meteor service that allows you to take advantage of the reactivity of Meteor’s front end.

angular.module(‘simple-todos’).controller(‘TodosListCtrl’, [‘$scope’, ‘$meteor’, function ($scope, $meteor) {    $scope.$meteorSubscribe(‘tasks’); 
$scope.tasks = $meteor.collection(function() {
return Tasks.find($scope.getReactively(‘query’), {sort:{createdAt: -1}}) });
$scope.addTask = function (newTask) {
$meteor.call(‘addTask’, newTask);
};
};

You can see that we can subscribe via component $scope

Also a similar theme to Blaze’s template subscriptions, calling $scope.$meteorSubscribe will automatically stop the subscription when the scope is destroyed.

React getMeteorData — The Future

Another framework given to us in Meteor 1.2. was React. The way we subscribe to data in React is similar to the methods described above. Meteor subscriptions in React are channeled through a mixin called ReactMeteorData.

getMeteorData will reactively rerun when the accessed data changes. getMeteorData must return an object, and the properties of the object will be copied onto the component’s this.data

Subscriptions that you make from getMeteorData using Meteor.subscribe will be automatically maintained across reruns of getMeteorData, and cleaned up when the component unmounts.

If you’d like to write ES2015 component syntax and still use these mixins, get the react-mixin using browserify. See below:

// COMPONENT CONTAINERExampleContainer = class ExampleContainer extends React.Component {
constructor() {
super();
this.onBtnClick = this.onBtnClick.bind(this);
}
getMeteorData() {
const handle = Meteor.subscribe('exampleSubscription');
const isReady = handle.ready()
const exampleData = Example.findOne();
return {handle, isReady, exampleData};
}

onBtnClick() {
alert("You clicked me!");
}
render() {
if (!this.data.isReady} {
return <Loading/>;
}
return (
<ExampleComponent
onBtnClick={this.onBtnClick}
item={this.data.exampleData}/>
)
}
}
reactMixin(ExampleContainer.prototype, ReactMeteorData);// STATELESS COMPONENT FUNCTIONExampleComponent = ({name, description, onBtnClick}) => {
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
<button onClick={onBtnClick}>Click Me!</button>
</div>
)
}

Conclusion

As you can see! We’ve come a long way! My point in all this is in trust your components. You can see in the beginning we were relying on a layer in the router that was very hard to reason about and became UNPREDICTABLE as the app scaled. When dealing with data management, we should always understand where our data comes from and how it flows. This is why in each of these examples, we put the data fetching responsibility on the components. In each form, we rely on a container component to fetch data then pass this data to our presentational components. Though each of the frameworks may have a different syntax, one theme remained true: it’s hard to manage the starting and stopping of subscriptions yourself. This is why Meteor created automatic unsubscribes when our components onDestroyed, $destroy, and unmount.

If you enjoyed this article, send me a recommend! Let’s chat this out further in the comments below!

--

--