Meteor: difference between names for collections, variables, publications, and subscriptions?

Let’s distinguish between the different names you might have to deal with when programming Meteor:

  • Variable names, such as Posts = new Meteor.Collection(...). These are used only so your code knows how to access this variable. Meteor doesn’t know or care what it is, although the convention is to capitalize.
  • Collection names, such as new Meteor.Collection("posts"). This maps to the name of a MongoDB collection (on the server) or a minimongo collection (on the client).
  • Publication and subscription names, used in Meteor.publish("foo", ...) or Meteor.subscribe("foo"). These have to match up for the client to subscribe to some data on the server.

There are two things you need to match up in the Meteor data model:

  1. Names of publications and their corresponding subscriptions
  2. (usually) Names of collections on the client and server, if using the default collection model

A subscription name needs to always match up with the name of a publication. However, the collections that are sent for a given subscription needn’t have anything to do with the subscription name. In fact, one can send over multiple cursors in one publication or one collection over different publications or even multiple subscriptions per publication, which appear merged as one in the client. You can also have different collection names in the server and client; read on…

Let’s review the different cases:

  1. Simple subscription model. This is the one you usually see in straightforward Meteor demos.

    On client and server,

    Posts = new Meteor.Collection("posts");
    

    On server only:

    Meteor.publish("postsPub", function() { 
        return Posts.find() 
    });
    

    On client only:

    Meteor.subscribe("postsPub")
    

    This synchronizes the Posts collection (which is named posts in the database) using the publication called postsPub.

  2. Multiple collections in one publication. You can send multiple cursors over for a single publication, using an array.

    On client and server:

    Posts = new Meteor.Collection("posts");
    Comments = new Meteor.Collection("comments");
    

    On server only:

    Meteor.publish("postsAndComments", function() { 
        return [ 
            Posts.find(), 
            Comments.find() 
        ]; 
    });
    

    On client only:

    Meteor.subscribe("postsAndComments");
    

    This synchronizes the Posts collection as well as the Comments collection using a single publication called postsAndComments. This type of publication is well-suited for relational data; for example, where you might want to publish only certain posts and the comments associated only with those posts. See a package that can build these cursors automatically.

  3. Multiple publications for a single collection. You can use multiple publications to send different slices of data for a single collection which are merged by Meteor automatically.

    On server and client:

    Posts = new Meteor.Collection("posts");
    

    On server only:

    Meteor.publish("top10Posts", function() { 
        return Posts.find({}, {
            sort: {comments: -1}, 
            limit: 10
        });
    });        
    Meteor.publish("newest10Posts", function() { 
        return Posts.find({}, {
            sort: {timestamp: -1},
            limit: 10
        }); 
    });
    

    On client only:

    Meteor.subscribe("top10Posts");
    Meteor.subscribe("newest10Posts");
    

    This pushes both the 10 posts with the most comments as well as the 10 newest posts on the site to the user, which sees both sets of data merged into a single Posts collection. If one of the newest posts is also a post with the most comments or vice versa, the Posts collection will contain less than 20 items. This is an example of how the data model in Meteor allows you to do powerful data merging operations without implementing the details yourself.

  4. Multiple subscriptions per publication. You can get multiple sets of data from the same publication using different arguments.

    On server and client:

    Posts = new Meteor.Collection("posts");
    

    On server only:

    Meteor.publish("postsByUser", function(user) { 
        return Posts.find({
            userId: user
        });
    });        
    

    On client only:

    Meteor.subscribe("postsByUser", "fooUser");
    Meteor.subscribe("postsByUser", "barUser");
    

    This causes the posts by fooUser and barUser to both show up in the posts collection. This model is convenient when you have several different computations that are looking at different slices of your data and may be updated dynamically. Note that when you subscribe inside a Deps.autorun(...), Meteor calls stop() on any previous subscription handle with the same name automatically, but if you are using these subscriptions outside of an autorun you will need to stop them yourself. As of right now, you can’t do two subscriptions with the same name inside an autorun computation, because Meteor can’t tell them apart.

  5. Pushing arbitrary data over a publication. You can completely customize publications to not require the same collection names on the server and client. In fact, the server can publish data that isn’t backed by a collection at all. To do this, you can use the API for the publish functions.

    On server only:

    Posts = new Meteor.Collection("posts"); 
    
    Meteor.publish("newPostsPub", function() {
        var sub = this;
        var subHandle = null;
    
        subHandle = Posts.find({}, {
            sort: {timestamp: -1},
            limit: 10
        })
        .observeChanges({
            added: function(id, fields) {
                sub.added("newposts", id, fields);            
            },
            changed: function(id, fields) {
                sub.changed("newposts", id, fields);            
            },
            removed: function(id) {
                sub.removed("newposts", id);
            }
        });
    
        sub.ready();
    
        sub.onStop(function() {
            subHandle.stop();
        })    
    });
    

    On client only:

    NewPosts = new Meteor.Collection("newposts");
    
    Meteor.subscribe("newPostsPub");
    

    This synchronizes the newest 10 posts from the Posts collection on the server (called posts in the database) to the NewPosts collection on the client (called newposts in minimongo) using the publication/subscription called newPostsPub. Note that observeChanges differs from observe, which can do a bunch of other things.

    The code seems complicated, but when you return a cursor inside a publish function, this is basically the code that Meteor is generating behind the scenes. Writing publications this way gives you a lot more control over what is and isn’t sent to the client. Be careful though, as you must manually turn off observe handles and mark when the subscription is ready. For more information, see Matt Debergalis’ description of this process (however, that post is out of date). Of course, you can combine this with the other pieces above to potentially get very nuanced and complicated publications.

Sorry for the essay 🙂 but many people get confused about this and I though it would be useful to describe all the cases.

Leave a Comment