The Joy of Publications and Subscriptions in Meteor.js

I LOVE Meteor. It is so addicting to be able to build a web application completely in JavaScript. Meteor is amazing because it does the difficult work for you (routing, web sockets, reactive data-binding, etc.), allowing you to focus on functionality. One of my favorite parts of Meteor is Publications and Subscriptions.

The publication API in Meteor allows you to securely publish from your server only the data from your Mongo database that you want your clients to have access to. Then on the client side you subscribe to the particular parts of the published data that you need at any one time. This way the client does not have access to data that is not meant for him/her, and their system is never overwhelmed with piles of data that are not relevant or necessary for the task they are trying to accomplish.

Out of the box, Meteor comes with a package called "autopublish" which automatically shares the full contents of all Mongo collections with the client. This gives them full read access to all of the data. You can imagine the significant security problems with using this approach. It's very important to turn this off and implement proper publications and subscriptions within your application.

So how do you publish and subscribe to data using Meteor? I will use an example from a project I am working on right now. This application is currently called CSL-Manager and is available on GitHub. It is a volunteer staff management application for use at my church. We have a youth drop-in center and we will use this to register new volunteers, log times and generate reports.

I have currently been working on the main dashboard page. When someone logs into the system, I want them to be able to see who else is currently checked in and have access to emergency contact info for each of those users should something go wrong. As you can see below, I have made a list on the page for users who are currently checked in. But since I have not yet published or subscribed to any user data, there is nothing to show.

First I need to go to my server folder and publish the data we want. In Meteor, any code in a folder called "server" is only run on the server. So in here I have a file called publications.js. The syntax in Meteor for publishing data is:

Meteor.publish('name', function);

The name is the name of the your publication and the function returns the data you want published. Now, if I was lazy and just wanted to dump all the user data, I would write:

Meteor.publish('checkedInUsers', function() {
	return Meteor.users.find();
});

But I have enough sense to know that would not be a good idea. So let's begin narrowing this down. First, I only want to publish the users that are currently checked in. So let's add a parameter to this publication:

Meteor.publish('checkedInUsers', function() {
	return Meteor.users.find({'profile.checkedIn': true});
});

Each user object in Meteor has a built-in profile property which contains an object for storing user profile information. Within my user profiles I have a property called checkedIn which is set to true when they check in and false when they check out of the system.

So now my server is only publishing the users whose checkedIn property is set to true. This is good, but not good enough. We still have a problem because even though we have limited which users are published, we are currently publishing all of the user data of those users. But only admins or managers should have access to a user's full data. In this case, we only need to publish some of each checked-in user's data. I am primarily interested in their name, their city of residence, their phone # and emergency contact information. So now I am going to specify which specific fields to publish:

Meteor.publish('checkedInUsers', function() {
	return Meteor.users.find({'profile.checkedIn' : true},{fields: {
		'username': true,
		'profile.firstName': true,
		'profile.lastName': true,
		'profile.city': true,
		'profile.state': true,
		'profile.phone': true,
		'profile.emergencyName1': true,
		'profile.emergencyPhone1': true,
		'profile.emergencyRelation1': true,
		'profile.emergencyName2': true,
		'profile.emergencyPhone2': true,
		'profile.emergencyRelation2': true,
		'profile.checkedIn': true
	}});
});

We have now published only the specific data we are interested in from only the users that are currently checked in. This is what we want. But the client still does not have any of this data because we have not yet subscribed to it on the client side.

The syntax in Meteor for subscribing to a publication is:

Meteor.subscribe(name, [arguments], callback);

In our case, I don't have any arguments to send or a callback function to run. So my publication call is:

Meteor.subscribe('checkedInUsers');

One nice thing about subscriptions is that you case place them anywhere. Sometimes you have data that needs to be shared with any visitor to your site, in that case it is enought to make a subscriptions.js file within your client folder. This data will always be subscribed to when a visitor arrives at your site. But I generally tie my subscriptions to my routes, so that a user will only subscribe to the data that is necessary for the page that they are currently visiting. In this case, I want the user to subscribe to the checkedInUsers data when they arrive at their dashboard.

In my Router.map I have a route called dashboard. I have place this subscription in the onBeforeAction function of this route so that the data will be subscribed to first-off when someone navigates to their dashboard.

Now, here is the most exciting part of publications and subscriptions... they are reactive! This means that as the data changes, the subscriptions are updated in real time! So in our case, once a user has subscribed to checkedInUsers, their data will be updated as different users check in and check out. I won't ever have to worry about checking for updated data because Meteor takes care of that for me. Three cheers for publications and subscriptions!

So now I can plug this data into my dashboard template and display it to my users. In my next blog post, I will show how to accomplish that using Spacebars, the built-in templating language of Meteor.

To view my full CSL-Manager project on GitHub, click here. For the official Meteor documentation on Publications and Subscriptions, click here.