What Goes Where: Making Sense of Meteor's Client/Server Split
One of Meteor’s biggest selling point is the way it blurs the line between client and server.
With Meteor, these two environments are no longer separate worlds: they share a common language, and can even share common code!
But that blurring of lines comes at a cost: it can sometimes be tricky to figure out what should run where.
Client vs Server
Try that with Ruby, Python, or C, and your browser will just look at you with a puzzled look on its face.
This is where Meteor comes in: by providing layers on top of both the server and the client, Meteor can bridge the gap and make the two sides talk to each other more smoothly.
Meteor’s Control Methods
Meteor gives you three basic ways of controlling where a file is executed:
- In your app, you can use the
/serverdirectories to restrict a file to only run in a specific environment.
- In packages, you can pass
"server"as arguments to the
api.addFile()function to only load a file in a specific environment.
You can also control where a code block inside a file is executed (supposing the file itself runs in both environment) thanks to the
For this article, we’ll only focus on the first scenario: controlling where a file is loaded using the
Meteor’s File Architecture
It’s also important to point out that Meteor doesn’t really care where you put your code past the client/server/both distinction. In other words, there is no practical difference between putting your code in
foo/bar.js and putting it in
That being said, if you need a little more structure we recommend checking out Microscope to see what a “typical” Meteor app looks like.
You can test this right now if you’d like; all it does is pop open a system dialog containing the string you passed as argument.
If your answer was something along the line of “What!? That doesn’t make any sense!” then congratulations, you got it right. This was a trick question:
alert() is clearly something that only makes sense in the context of the browser, since it needs a system to open the dialog, a screen to render it on, and a user to close it.
Now consider the following, used to set the scroll position of the
body of a page to
300px from the top:
Additionally, there’s another category of code that should only run on the client: code that targets the current user.
Again, it makes sense if you think about it: the server is in charge of serving your app to hundreds, if not thousands of users. It doesn’t have a “current” user.
The Method Exception
The only exception to the “server doesn’t know about the user” rule is Meteor Methods.
A Method is a function that runs on the server, but is called from the client. This means the server knows which specific user made the call, and this is why
Meteor.userId() (as well as the equivalent
this.userId) can be used inside Methods code, even on the server.
There’s another Meteor feature that’s specific to the current user: Session variables.
In fact, the same can be said about client-side variables in general: even if client-side variables were somehow sent back to the server (and they aren’t), it wouldn’t really make sense. If Alice defines
x equal to
foo but Bob sets it equal to
bar, how is the server supposed to know which one to use?
[Client] Templates & Stylesheets
Although this might change in the future, both templates and stylesheets are currently only executed on the client.
The one exception to this is email template: since emails are sent out by the server, you can use a package such as handlebars-server to define specific, server-only templates and styles.
So if templates, stylesheets, template-related code, user-specific code, and browser-related code all run on the client, what does that leave for the server?
A couple important things, as you’ll soon see. But before we can get there, we need to talk about code that needs to run on both the client and server.
The main example of this is collections code. Another one of Meteor’s innovations is the ability to mirror some of your data on the client, and this is done through the afore-mentioned collections.
For that reason, it makes sense to define collections in both environments (in other words, in any directory besides
/server). Of course, collection-related code (such as helpers or validation code) will also come along for the ride.
Meteor also features something called “optimistic UI” (a.k.a. latency compensation). This is a fancy way of saying that if an operation needs to involve the server (which would necessarily slow it down), the client will try to complete that operation by itself first, and then amend the result if needed once it gets the server’s actual reply.
Yet this can only happen if the client knows what the operation consists of, which is why method code is often shared among both environments.
[Server] Secure Data
Finally, we get to the server, and just the server.
Unlike the client, the server has direct access to your whole database, which gives it more control over your data.
For example, one of the main tasks of the server is publishing data to the client. This is done through publications, which are server-only. Since publications control which subset of your data should be made public (and which subset should remain secure), it stands to reason that this is not something you want your users to be able to control.
In fact, generally speaking any operation that involves private data like email addresses or API keys will be best handled by the server. After all, you wouldn’t want to expose that data to any random user!
[Server] Time-Sensitive Operations
Finally, another category of server-only operations is anything involving time: it’s trivial for a user to fake their browser’s time (not to mention the mess involved with timezones), so you can’t rely on it for any serious timekeeping.
So cronjob-type operations that needs to happen at regular intervals, rate-limiting, and timestamping should all be left to the server.
A Real-World Example
Let’s bring all of this together by looking at the code of Microscope, the app that you build in Discover Meteor.
client directory contains the following items:
helperscontains global template helpers and code for other client-only features such as error messages.
stylesheetscontains the app’s styles.
main.jshold the app’s main template and its code.
lib folder contains files shared across client and server:
collectionscontains the code for the Posts, Comments, and Notifications collections.
permissions.jscontains code used to define who can do what, which applies both to client and server.
router.jsdeals with client-side and server-side routes.
server contains server-only code:
fixtures.jscontains code that will seed our database with content the first time we run the app.
pubications.jscontains our publication code.
This is by no means meant to be an exhaustive list of every single use case you’ll encounter while building Meteor apps.
So I strongly recommend checking out the Meteor documentation whenever you’re not sure if something should run on the client, server, or both.
But hopefully, it will be enough to give you a few tools to help you figure things out the next time you’re stuck at that “Save As…” dialog wondering where to put your code.