You are on page 1of 270

Table

of Contents
Introduction 1.1
Introduction 1.2
Code style 1.3
Application Structure 1.4
Migrating to Meteor 1.4 1.5
Migrating to Meteor 1.3 1.6
Collections and Schemas 1.7
Publications and Data Loading 1.8
Methods 1.9
Users and Accounts 1.10
Testing 1.11
URLs and Routing 1.12
User Interfaces 1.13
Blaze 1.14
React 1.15
Angular 1.16
Atmosphere vs. npm 1.17
Using Atmosphere Packages 1.18
Writing Atmosphere Packages 1.19
Using npm Packages 1.20
Writing npm Packages 1.21
Mobile 1.22
Build system 1.23
Security 1.24
Deployment and Monitoring 1.25

1
Introduction

Meteor Guide
The Gitbook version of the Meteor Guide http://guide.meteor.com/

2
Introduction

What is Meteor?
Meteor is a full-stack JavaScript platform for developing modern web and mobile
applications. Meteor includes a key set of technologies for building connected-client reactive
applications, a build tool, and a curated set of packages from the Node.js and general
JavaScript community.

Meteor allows you to develop in one language, JavaScript, in all environments:


application server, web browser, and mobile device.

Meteor uses data on the wire, meaning the server sends data, not HTML, and the
client renders it.

Meteor embraces the ecosystem, bringing the best parts of the extremely active
JavaScript community to you in a careful and considered way.

Meteor provides full stack reactivity, allowing your UI to seamlessly reflect the true
state of the world with minimal development effort.

Quick start
Meteor supports OS X, Windows, and Linux.

On Windows? Download the official Meteor installer here.

On OS X or Linux? Install the latest official Meteor release from your terminal:

curl https://install.meteor.com/ | sh

The Windows installer supports Windows 7, Windows 8.1, Windows Server 2008, and
Windows Server 2012. The command line installer supports Mac OS X 10.7 (Lion) and
above, and Linux on x86 and x86_64 architectures.

Once you've installed Meteor, create a project:

meteor create myapp

Run it locally:

3
Introduction

cd myapp
meteor npm install
meteor
# Meteor server running on: http://localhost:3000/

Meteor comes with npm bundled so that you can type meteor npm without worrying
about installing it yourself. If you like, you can also use a globally installed npm to
manage your packages.

Meteor resources
1. The place to get started with Meteor is the official tutorial.

2. Stack Overflow is the best place to ask (and answer!) technical questions. Be sure to
add the meteor tag to your question.

3. Visit the Meteor discussion forums to announce projects, get help, talk about the
community, or discuss changes to core.

4. The Meteor docs is the best place to find the core API documentation of the platform.

5. Atmosphere is the repository of community packages designed especially for Meteor.

6. Awesome Meteor is a community-curated list of packages and resources.

What is the Meteor Guide?


This is a set of articles outlining opinions on best-practice application development using the
Meteor platform. Our aim is to cover patterns that are common to the development of all
modern web and mobile applications, so many concepts documented here are not
necessarily Meteor specific and could be applied to any application built with a focus on
modern, interactive user interfaces.

Nothing in the Meteor guide is required to build a Meteor application---you can certainly use
the platform in ways that contradict the principles and patterns of the guide. However, the
guide is an attempt to document best practices and community conventions, so we hope that
the majority of the Meteor community will benefit from adopting the practices documented
here.

The APIs of the Meteor platform are available at the docs site, and you can browse
community packages on atmosphere.

4
Introduction

Target audience
The guide is targeted towards intermediate developers that have some familiarity with
JavaScript, the Meteor platform, and web development in general. If you are just getting
started with Meteor, we recommend starting with the official tutorial.

Example app
Many articles reference the Todos example application. This code is being actively
developed alongside the guide. You can see the latest source code for the app, and file
issues or make suggestions via pull request at its GitHub repository.

Guide development
Contributing
Ongoing Meteor Guide development takes place in the open on GitHub. We encourage pull
requests and issues to discuss problems with and changes that could be made to the
content. We hope that keeping our process open and honest will make it clear what we plan
to include in the guide and what changes will be coming in future Meteor versions.

Goals of the project


The decisions made and practices outlined in the guide must necessarily be opinionated.
Certain best practices will be highlighted and other valid approaches ignored. We aim to
reach community consensus around major decisions but there will always be other ways to
solve problems when developing your application. We believe it's important to know what the
"standard" way to solve a problem is before branching out to other options. If an alternate
approach proves itself superior, then it should make its way into a future version of the
guide.

An important function of the guide is to shape future development in the Meteor platform.
By documenting best practices, the guide shines a spotlight on areas of the platform that
could be better, easier, or more performant, and thus will be used to focus a lot of future
platform choices.

Similarly, gaps in the platform highlighted by the guide can often be plugged by community
packages; we hope that if you see an opportunity to improve the Meteor workflow by writing
a package, that you take it! If you're not sure how best to design or architect your package,
reach out on the forums and start a discussion.

5
Introduction

6
Code style

After reading this article, you'll know:

1. Why it's a good idea to have consistent code style


2. Which style guide we recommend for JavaScript code
3. How to set up ESLint to check code style automatically
4. Style suggestions for Meteor-specific patterns, such as Methods, publications, and more

Benefits of consistent style


Countless hours have been spent by developers throughout the years arguing over single
vs. double quotes, where to put brackets, how many spaces to type, and all kinds of other
cosmetic code style questions. These are all questions that have at best a tangential
relationship to code quality, but are very easy to have opinions about because they are so
visual.

While it's not necessarily important whether your code base uses single or double quotes for
string literals, there are huge benefits to making that decision once and having it be
consistent across your organization. These benefits also apply to the Meteor and JavaScript
development communities as a whole.

Easy to read code


The same way that you don't read English sentences one word at a time, you don't read
code one token at a time. Mostly you just look at the shape of a certain expression, or the
way it highlights in your editor, and assume what it does. If the style of every bit of code is
consistent, that ensures that bits of code that look the same actually are the same - there
isn't any hidden punctuation or gotchas that you don't expect, so you can focus on
understanding the logic instead of the symbols. One example of this is indentation - while in
JavaScript, indentation is not meaningful, it's helpful to have all of your code consistently
indented so that you don't need to read all of the brackets in detail to see what is going on.

// This code is misleading because it looks like both statements


// are inside the conditional.
if (condition)
firstStatement();
secondStatement();

7
Code style

// Much clearer!
if (condition) {
firstStatement();
}

secondStatement();

Automatic error checking


Having a consistent style means that it's easier to adopt standard tools for error checking.
For example, if you adopt a convention that you must always use let or const instead of
var , you can now use a tool to ensure all of your variables are scoped the way you expect.

That means you can avoid bugs where variables act in unexpected ways. Also, by enforcing
that all variables are declared before use, you can easily catch typos before even running
any code!

Deeper understanding
It's hard to learn everything about a programming language at once. For example,
programmers new to JavaScript often struggle with the var keyword and function scope.
Using a community-recommended coding style with automatic linting can warn you about
these pitfalls proactively. This means you can jump right into coding without learning about
all of the edge cases of JavaScript ahead of time.

As you write more code and come up against the recommended style rules, you can take
that as an opportunity to learn more about your programming language and how different
people prefer to use it.

JavaScript style guide


Here at Meteor, we strongly believe that JavaScript is the best language to build web
applications, for a variety of reasons. JavaScript is constantly improving, and the standards
around ES2015 have really brought together the JavaScript community. Here are our
recommendations about how to use ES2015 JavaScript in your app today.

8
Code style

An example of refactoring from JavaScript to ES2015

Use the `ecmascript` package


ECMAScript, the language standard on which every browser's JavaScript implementation is
based, has moved to yearly standards releases. The newest complete standard is ES2015,
which includes some long-awaited and very significant improvements to the JavaScript
language. Meteor's ecmascript package compiles this standard down to regular JavaScript
that all browsers can understand using the popular Babel compiler. It's fully backwards
compatible to "regular" JavaScript, so you don't have to use any new features if you don't
want to. We've put a lot of effort into making advanced browser features like source maps
work great with this package, so that you can debug your code using your favorite developer
tools without having to see any of the compiled output.

The ecmascript package is included in all new apps and packages by default, and compiles
all files with the .js file extension automatically. See the list of all ES2015 features
supported by the ecmascript package.

To get the full experience, you should also use the es5-shim package which is included in
all new apps by default. This means you can rely on runtime features like Array#forEach
without worrying about which browsers support them.

All of the code samples in this guide and future Meteor tutorials will use all of the new
ES2015 features. You can also read more about ES2015 and how to get started with it on
the Meteor Blog:

Getting started with ES2015 and Meteor


Set up Sublime Text for ES2015
How much does ES2015 cost?

9
Code style

Follow a JavaScript style guide


We recommend choosing and sticking to a JavaScript style guide and enforcing it with tools.
A popular option that we recommend is the Airbnb style guide with the ES6 extensions (and
optionally React extensions).

Check your code with ESLint


"Code linting" is the process of automatically checking your code for common errors or style
problems. For example, ESLint can determine if you have made a typo in a variable name,
or some part of your code is unreachable because of a poorly written if condition.

We recommend using the Airbnb eslint configuration which verifies the Airbnb styleguide.

Below, you can find directions for setting up automatic linting at many different stages of
development. In general, you want to run the linter as often as possible, because it's the
fastest and easiest way to identify typos and small errors.

Installing and running ESLint


To setup ESLint in your application, you can install the following npm packages:

meteor npm install --save-dev babel-eslint eslint-config-airbnb eslint-plugin-import e


slint-plugin-meteor eslint-plugin-react eslint-plugin-jsx-a11y eslint-import-resolver-
meteor eslint

Meteor comes with npm bundled so that you can type meteor npm without worrying
about installing it yourself. If you like, you can also use a globally installed npm
command.

You can also add a eslintConfig section to your package.json to specify that you'd like to
use the Airbnb config, and to enable ESLint-plugin-Meteor. You can also setup any extra
rules you want to change, as well as adding a lint npm command:

10
Code style

{
...
"scripts": {
"lint": "eslint .",
"pretest": "npm run lint --silent"
},
"eslintConfig": {
"parser": "babel-eslint",
"parserOptions": {
"allowImportExportEverywhere": true
},
"plugins": [
"meteor"
],
"extends": [
"airbnb",
"plugin:meteor/recommended"
],
"settings": {
"import/resolver": "meteor"
},
"rules": {}
}
}

To run the linter, you can now simply type:

meteor npm run lint

For more details, read the Getting Started directions from the ESLint website.

Integrating with your editor


Linting is the fastest way to find potential bugs in your code. Running a linter is usually faster
than running your app or your unit tests, so it's a good idea to run it all the time. Setting up
linting in your editor can seem annoying at first since it will complain often when you save
poorly-formatted code, but over time you'll develop the muscle memory to just write well-
formatted code in the first place. Here are some directions for setting up ESLint in different
editors:

Sublime Text
You can install the Sublime Text packages that integrate them into the text editor. It's
generally recommended to use Package Control to add these packages. If you already have
that setup, you can just add the these packages by name; if not, click the instructions links:

11
Code style

Babel (for syntax highlighting – full instructions)


SublimeLinter (full instructions)
SublimeLinter-contrib-eslint (full instructions)

To get proper syntax highlighting, go to a .js file, then select the following through the View
dropdown menu: Syntax -> Open all with current extension as... -> Babel -> JavaScript
(Babel). If you are using React .jsx files, do the same from a .jsx file. If it's working, you will
see "JavaScript (Babel)" in the lower right hand corner of the window when you are on one
of these files. Refer to the package readme for information on compatible color schemes.

A side note for Emmet users: You can use \ to expand HTML tags in .jsx files, and it will
correctly expand classes to React's "className" property. You can bind to the tab key for
this, but you may not want to.

Atom
Using ESLint with Atom is simple. Just install these three packages:

apm install language-babel


apm install linter
apm install linter-eslint

Then restart (or reload by pressing Ctrl+Alt+R / Cmd+Opt+R) Atom to activate linting.

WebStorm
WebStorm provides these instructions for using ESLint. After you install the ESLint Node
packages and set up your package.json , just enable ESLint and click "Apply". You can
configure how WebStorm should find your .eslintrc file, but on my machine it worked
without any changes. It also automatically suggested switching to "JSX Harmony" syntax
highlighting.

12
Code style

Linting can be activated on WebStorm on a project-by-project basis, or you can set ESLint
as a default under Editor > Inspections, choosing the Default profile, checking "ESLint", and
applying.

Visual Studio Code


Using ESLint in VS Code requires installation of the 3rd party ESLint extension. In order to
install the extension, follow these steps:

1. Launch VS Code and open the quick open menu by typing Ctrl+P
2. Paste ext install vscode-eslint in the command window and press Enter
3. Restart VS Code

Meteor code style


The section above talked about JavaScript code in general - you can easily apply it in any
JavaScript application, not just with Meteor apps. However, there are some style questions
that are Meteor-specific, in particular how to name and structure all of the different
components of your app.

Collections
Collections should be named as a plural noun, in PascalCase. The name of the collection in
the database (the first argument to the collection constructor) should be the same as the
name of the JavaScript symbol.

13
Code style

// Defining a collection
Lists = new Mongo.Collection('Lists');

Fields in the database should be camelCased just like your JavaScript variable names.

// Inserting a document with camelCased field names


Widgets.insert({
myFieldName: 'Hello, world!',
otherFieldName: 'Goodbye.'
});

Methods and publications


Method and publication names should be camelCased, and namespaced to the module they
are in:

// in imports/api/todos/methods.js
updateText = new ValidatedMethod({
name: 'todos.updateText',
// ...
});

Note that this code sample uses the ValidatedMethod package recommended in the
Methods article. If you aren't using that package, you can use the name as the property
passed to Meteor.methods .

Here's how this naming convention looks when applied to a publication:

// Naming a publication
Meteor.publish('lists.public', function listsPublic() {
// ...
});

Files, exports, and packages


You should use the ES2015 import and export features to manage your code. This will
let you better understand the dependencies between different parts of your code, and it will
be easy to know where to look if you need to read the source code of a dependency.

Each file in your app should represent one logical module. Avoid having catch-all utility
modules that export a variety of unrelated functions and symbols. Often, this can mean that
it's good to have one class, UI component, or collection per file, but there are cases where it

14
Code style

is OK to make an exception, for example if you have a UI component with a small sub-
component that isn't used outside of that file.

When a file represents a single class or UI component, the file should be named the same
as the thing it defines, with the same capitalization. So if you have a file that exports a class:

export default class ClickCounter { ... }

This class should be defined inside a file called ClickCounter.js . When you import it, it'll
look like this:

import ClickCounter from './ClickCounter.js';

Note that imports use relative paths, and include the file extension at the end of the file
name.

For Atmosphere packages, as the older pre-1.3 api.export syntax allowed more than one
export per package, you'll tend to see non-default exports used for symbols. For instance:

// You'll need to destructure here, as Meteor could export more symbols


import { Meteor } from 'meteor/meteor';

// This will not work


import Meteor from 'meteor/meteor';

Templates and components


Since Spacebars templates are always global, can't be imported and exported as modules,
and need to have names that are completely unique across the whole app, we recommend
naming your Blaze templates with the full path to the namespace, separated by
underscores. Underscores are a great choice in this case because then you can easily type
the name of the template as one symbol in JavaScript.

<template name="Lists_show">
...
</template>

If this template is a "smart" component that loads server data and accesses the router,
append _page to the name:

15
Code style

<template name="Lists_show_page">
...
</template>

Often when you are dealing with templates or UI components, you'll have several closely
coupled files to manage. They could be two or more of HTML, CSS, and JavaScript files. In
this case, we recommend putting these together in the same directory with the same name:

# The Lists_show template from the Todos example app has 3 files:
show.html
show.js
show.less

The whole directory or path should indicate that these templates are related to the Lists
module, so it's not necessary to reproduce that information in the file name. Read more
about directory structure below.

If you are writing your UI in React, you don't need to use the underscore-split names
because you can import and export your components using the JavaScript module system.

16
Application Structure

After reading this article, you'll know:

1. How a Meteor application compares to other types of applications in terms of file


structure.
2. How to organize your application both for small and larger applications.
3. How to format your code and name the parts of your application in consistent and
maintainable ways.

Universal JavaScript
Meteor is a full-stack framework for building JavaScript applications. This means Meteor
applications differ from most applications in that they include code that runs on the client,
inside a web browser or Cordova mobile app, code that runs on the server, inside a Node.js
container, and common code that runs in both environments. The Meteor build tool allows
you to easily specify what JavaScript code, including any supporting UI templates, CSS
rules, and static assets, to run in each environment using a combination of ES2015 import
and export and the Meteor build system default file load order rules.

ES2015 modules
As of version 1.3, Meteor ships with full support for ES2015 modules. The ES2015 module
standard is the replacement for CommonJS and AMD, which are commonly used JavaScript
module format and loading systems.

In ES2015, you can make variables available outside a file using the export keyword. To
use the variables somewhere else, you must import them using the path to the source.
Files that export some variables are called "modules", because they represent a unit of
reusable code. Explicitly importing the modules and packages you use helps you write your
code in a modular way, avoiding the introduction of global symbols and "action at a
distance".

Since this is a new feature introduced in Meteor 1.3, you will find a lot of code online that
uses the older, more centralized conventions built around packages and apps declaring
global symbols. This old system still works, so to opt-in to the new module system code
must be placed inside the imports/ directory in your application. We expect a future
release of Meteor will turn on modules by default for all code, because this is more aligned
with how developers in the wider JavaScript community write their code.

You can read about the module system in detail in the modules package README. This
package is automatically included in every new Meteor app as part of the ecmascript meta-
package, so most apps won't need to do anything to start using modules right away.

17
Application Structure

Introduction to using `import` and `export`


Meteor allows you to import not only JavaScript in your application, but also CSS and
HTML to control load order:

import '../../api/lists/methods.js'; // import from relative path


import '/imports/startup/client'; // import module with index.js from absolute path

import './loading.html'; // import Blaze compiled HTML from relative path


import '/imports/ui/style.css'; // import CSS from absolute path

For more ways to import styles, see the Build System article.

Meteor also supports the standard ES2015 modules export syntax:

export const listRenderHold = LaunchScreen.hold(); // named export


export { Todos }; // named export
export default Lists; // default export
export default new Collection('lists'); // default export

Importing from packages


In Meteor, it is also simple and straightforward to use the import syntax to load npm
packages on the client or server and access the package's exported symbols as you would
with any other module. You can also import from Meteor Atmosphere packages, but the
import path must be prefixed with meteor/ to avoid conflict with the npm package
namespace. For example, to import moment from npm and HTTP from Atmosphere:

import moment from 'moment'; // default import from npm


import { HTTP } from 'meteor/http'; // named import from Atmosphere

For more details using imports with packages see Using Packages in the Meteor Guide.

Using `require`
In Meteor, import statements compile to CommonJS require syntax. However, as a
convention we encourage you to use import .

With that said, in some situations you may need to call out to require directly. One notable
example is when requiring client or server-only code from a common file. As import s must
be at the top-level scope, you may not place them within an if statement, so you'll need to
write code like:

18
Application Structure

if (Meteor.isClient) {
require('./client-only-file.js');
}

Note that dynamic calls to require() (where the name being required can change at
runtime) cannot be analyzed correctly and may result in broken client bundles.

If you need to require from an ES2015 module with a default export, you can grab the
export with require("package").default .

Another situation where you'll need to use require is in CoffeeScript files. As CS doesn't
support the import syntax yet, you should use require :

{ FlowRouter } = require 'meteor/kadira:flow-router'


React = require 'react'

Exporting with CoffeeScript


When using CoffeeScript, not only the syntax to import variables is different, but also the
export has to be done in a different way. Variables to be exported are put in the exports
object:

exports.Lists = ListsCollection 'Lists'

File structure
To fully use the module system and ensure that our code only runs when we ask it to, we
recommend that all of your application code should be placed inside the imports/ directory.
This means that the Meteor build system will only bundle and include that file if it is
referenced from another file using an import (also called "lazy evaluation or loading").

Meteor will load all files outside of any directory named imports/ in the application using
the default file load order rules (also called "eager evaluation or loading"). It is recommended
that you create exactly two eagerly loaded files, client/main.js and server/main.js , in
order to define explicit entry points for both the client and the server. Meteor ensures that
any file in any directory named server/ will only be available on the server, and likewise for
files in any directory named client/ . This also precludes trying to import a file to be used
on the server from any directory named client/ even if it is nested in an imports/
directory and vice versa for importing client files from server/ .

19
Application Structure

These main.js files won't do anything themselves, but they should import some startup
modules which will run immediately, on client and server respectively, when the app loads.
These modules should do any configuration necessary for the packages you are using in
your app, and import the rest of your app's code.

Example directory layout


To start, let's look at our Todos example application, which is a great example to follow when
structuring your app. Here's an overview of its directory structure:

imports/
startup/
client/
index.js # import client startup through a single index entry po
int
routes.js # set up all routes in the app
useraccounts-configuration.js # configure login templates
server/
fixtures.js # fill the DB with example data on startup
index.js # import server startup through a single index entry po
int

api/
lists/ # a unit of domain logic
server/
publications.js # all list-related publications
publications.tests.js # tests for the list publications
lists.js # definition of the Lists collection
lists.tests.js # tests for the behavior of that collection
methods.js # methods related to lists
methods.tests.js # tests for those methods

ui/
components/ # all reusable components in the application
# can be split by domain if there are many
layouts/ # wrapper components for behaviour and visuals
pages/ # entry points for rendering used by the router

client/
main.js # client entry point, imports all client code

server/
main.js # server entry point, imports all server code

Structuring imports

20
Application Structure

Now that we have placed all files inside the imports/ directory, let's think about how best to
organize our code using modules. It makes sense to put all code that runs when your app
starts in an imports/startup directory. Another good idea is splitting data and business
logic from UI rendering code. We suggest using directories called imports/api and
imports/ui for this logical split.

Within the imports/api directory, it's sensible to split the code into directories based on the
domain that the code is providing an API for --- typically this corresponds to the collections
you've defined in your app. For instance in the Todos example app, we have the
imports/api/lists and imports/api/todos domains. Inside each directory we define the

collections, publications and methods used to manipulate the relevant domain data.

Note: in a larger application, given that the todos themselves are a part of a list, it might
make sense to group both of these domains into a single larger "list" module. The
Todos example is small enough that we need to separate these only to demonstrate
modularity.

Within the imports/ui directory it typically makes sense to group files into directories based
on the type of UI side code they define, i.e. top level pages , wrapping layouts , or reusable
components .

For each module defined above, it makes sense to co-locate the various auxiliary files with
the base JavaScript file. For instance, a Blaze UI component should have its template
HTML, JavaScript logic, and CSS rules in the same directory. A JavaScript module with
some business logic should be co-located with the unit tests for that module.

Startup files
Some of your code isn't going to be a unit of business logic or UI, it's just some setup or
configuration code that needs to run in the context of the app when it starts up. In the Todos
example app, the imports/startup/client/useraccounts-configuration.js file configures the
useraccounts login templates (see the Accounts article for more information about

useraccounts ). The imports/startup/client/routes.js configures all of the routes and then

imports all other code that is required on the client:

21
Application Structure

import { FlowRouter } from 'meteor/kadira:flow-router';


import { BlazeLayout } from 'meteor/kadira:blaze-layout';
import { AccountsTemplates } from 'meteor/useraccounts:core';

// Import to load these templates


import '../../ui/layouts/app-body.js';
import '../../ui/pages/root-redirector.js';
import '../../ui/pages/lists-show-page.js';
import '../../ui/pages/app-not-found.js';

// Import to override accounts templates


import '../../ui/accounts/accounts-templates.js';

// Below here are the route definitions

We then import both of these files in imports/startup/client/index.js :

import './useraccounts-configuration.js';
import './routes.js';

This makes it easy to then import all the client startup code with a single import as a module
from our main eagerly loaded client entry point client/main.js :

import '/imports/startup/client';

On the server, we use the same technique of importing all the startup code in
imports/startup/server/index.js :

// This defines a starting set of data to be loaded if the app is loaded with an empty
db.
import '../imports/startup/server/fixtures.js';

// This file configures the Accounts package to define the UI of the reset password em
ail.
import '../imports/startup/server/reset-password-email.js';

// Set up some rate limiting and other important security settings.


import '../imports/startup/server/security.js';

// This defines all the collections, publications and methods that the application pro
vides
// as an API to the client.
import '../imports/api/api.js';

22
Application Structure

Our main server entry point server/main.js then imports this startup module. You can see
that here we don't actually import any variables from these files - we just import them so that
they execute in this order.

Importing Meteor "pseudo-globals"


For backwards compatibility Meteor 1.3 still provides Meteor's global namespacing for the
Meteor core package as well as for other Meteor packages you include in your application.
You can also still directly call functions such as Meteor.publish , as in previous versions of
Meteor, without first importing them. However, it is recommended best practice that you first
load all the Meteor "pseudo-globals" using the import { Name } from 'meteor/package'
syntax before using them. For instance:

import { Meteor } from 'meteor/meteor';


import { EJSON } from 'meteor/ejson';

Default file load order


Even though it is recommended that you write your application to use ES2015 modules and
the imports/ directory, Meteor 1.3 continues to support eager loading of files, using these
default load order rules, to provide backwards compatibility with applications written for
Meteor 1.2 and earlier. You may combine both eager loading and lazy loading using import
in a single app. Any import statements are evaluated in the order they are listed in a file
when that file is loaded and evaluated using these rules.

There are several load order rules. They are applied sequentially to all applicable files in the
application, in the priority given below:

1. HTML template files are always loaded before everything else


2. Files beginning with main. are loaded last
3. Files inside any lib/ directory are loaded next
4. Files with deeper paths are loaded next
5. Files are then loaded in alphabetical order of the entire path

23
Application Structure

nav.html
main.html
client/lib/methods.js
client/lib/styles.js
lib/feature/styles.js
lib/collections.js
client/feature-y.js
feature-x.js
client/main.js

For example, the files above are arranged in the correct load order. main.html is loaded
second because HTML templates are always loaded first, even if it begins with main. , since
rule 1 has priority over rule 2. However, it will be loaded after nav.html because rule 2 has
priority over rule 5.

client/lib/styles.js and lib/feature/styles.js have identical load order up to rule 4;

however, since client comes before lib alphabetically, it will be loaded first.

You can also use Meteor.startup to control when run code is run on both the server and
the client.

Special directories
By default, any JavaScript files in your Meteor application folder are bundled and loaded on
both the client and the server. However, the names of the files and directories inside your
project can affect their load order, where they are loaded, and some other characteristics.
Here is a list of file and directory names that are treated specially by Meteor:

imports

Any directory named imports/ is not loaded anywhere and files must be imported
using import .

node_modules

Any directory named node_modules/ is not loaded anywhere. node.js packages


installed into node_modules directories must be imported using import or by using
Npm.depends in package.js .

client

Any directory named client/ is not loaded on the server. Similar to wrapping your
code in if (Meteor.isClient) { ... } . All files loaded on the client are automatically
concatenated and minified when in production mode. In development mode, JavaScript

24
Application Structure

and CSS files are not minified, to make debugging easier. CSS files are still combined
into a single file for consistency between production and development, because
changing the CSS file's URL affects how URLs in it are processed.

HTML files in a Meteor application are treated quite a bit differently from a server-
side framework. Meteor scans all the HTML files in your directory for three top-level
elements: <head> , <body> , and <template> . The head and body sections are
separately concatenated into a single head and body, which are transmitted to the
client on initial page load.

server

Any directory named server/ is not loaded on the client. Similar to wrapping your code
in if (Meteor.isServer) { ... } , except the client never even receives the code. Any
sensitive code that you don't want served to the client, such as code containing
passwords or authentication mechanisms, should be kept in the server/ directory.

Meteor gathers all your JavaScript files, excluding anything under the client , public ,
and private subdirectories, and loads them into a Node.js server instance. In Meteor,
your server code runs in a single thread per request, not in the asynchronous callback
style typical of Node.

public

All files inside a top-level directory called public/ are served as-is to the client. When
referencing these assets, do not include public/ in the URL, write the URL as if they
were all in the top level. For example, reference public/bg.png as <img src='/bg.png'
/> . This is the best place for favicon.ico , robots.txt , and similar files.

private

All files inside a top-level directory called private/ are only accessible from server
code and can be loaded via the Assets API. This can be used for private data files and
any files that are in your project directory that you don't want to be accessible from the
outside.

client/compatibility

This folder is for compatibility with JavaScript libraries that rely on variables declared
with var at the top level being exported as globals. Files in this directory are executed
without being wrapped in a new variable scope. These files are executed before other
client-side JavaScript files.

It is recommended to use npm for 3rd party JavaScript libraries and use import to
control when files are loaded.

25
Application Structure

tests

Any directory named tests/ is not loaded anywhere. Use this for any test code you
want to run using a test runner outside of Meteor's built-in test tools.

The following directories are also not loaded as part of your app code:

Files/directories whose names start with a dot, like .meteor and .git
packages/ : Used for local packages

cordova-build-override/ : Used for advanced mobile build customizations

programs : For legacy reasons

Files outside special directories


All JavaScript files outside special directories are loaded on both the client and the server.
Meteor provides the variables Meteor.isClient and Meteor.isServer so that your code can
alter its behavior depending on whether it's running on the client or the server.

CSS and HTML files outside special directories are loaded on the client only and cannot be
used from server code.

Splitting into multiple apps


If you are writing a sufficiently complex system, there can come a time where it makes sense
to split your code up into multiple applications. For example you may want to create a
separate application for the administration UI (rather than checking permissions all through
the admin part of your site, you can check once), or separate the code for the mobile and
desktop versions of your app.

Another very common use case is splitting a worker process away from your main
application so that expensive jobs do not impact the user experience of your visitors by
locking up a single web server.

There are some advantages of splitting your application in this way:

Your client JavaScript bundle can be significantly smaller if you separate out code that a
specific type of user will never use.

You can deploy the different applications with different scaling setups and secure them
differently (for instance you might restrict access to your admin application to users
behind a firewall).

You can allow different teams at your organization to work on the different applications
independently.

26
Application Structure

However there are some challenges to splitting your code in this way that should be
considered before jumping in.

Sharing code
The primary challenge is properly sharing code between the different applications you are
building. The simplest approach to deal with this issue is to simply deploy the same
application on different web servers, controlling the behavior via different settings. This
approach allows you to easily deploy different versions with different scaling behavior but
doesn't enjoy most of the other advantages stated above.

If you want to create Meteor applications with separate code, you'll have some modules that
you'd like to share between them. If those modules are something the wider world could use,
you should consider publishing them to a package system, either npm or Atmosphere,
depending on whether the code is Meteor-specific or otherwise.

If the code is private, or of no interest to others, it typically makes sense to simply include
the same module in both applications (you can do this with private npm modules). There are
several ways to do this:

a straightforward approach is simply to include the common code as a git submodule of


both applications.

alternatively, if you include both applications in a single repository, you can use symbolic
links to include the common module inside both apps.

Sharing data
Another important consideration is how you'll share the data between your different
applications.

The simplest approach is to point both applications at the same MONGO_URL and allow both
applications to read and write from the database directly. This works well thanks to Meteor's
support for reactivity through the database. When one app changes some data in MongoDB,
users of any other app connected to the database will see the changes immediately thanks
to Meteor's livequery.

However, in some cases it's better to allow one application to be the master and control
access to the data for other applications via an API. This can help if you want to deploy the
different applications on different schedules and need to be conservative about how the data
changes.

27
Application Structure

The simplest way to provide a server-server API is to use Meteor's built-in DDP protocol
directly. This is the same way your Meteor client gets data from your server, but you can also
use it to communicate between different applications. You can use DDP.connect() to
connect from a "client" server to the master server, and then use the connection object
returned to make method calls and read from publications.

Sharing accounts
If you have two servers that access the same database and you want authenticated users to
make DDP calls across the both of them, you can use the resume token set on one
connection to login on the other.

If your user has connected to server A, then you can use DDP.connect() to open a
connection to server B, and pass in server A's resume token to authenticate on server B. As
both servers are using the same DB, the same server token will work in both cases. The
code to authenticate looks like this:

// This is server A's token as the default `Accounts` points at our server
const token = Accounts._storedLoginToken();

// We create a *second* accounts client pointing at server B


const app2 = DDP.connect('url://of.server.b');
const accounts2 = new AccountsClient({ connection: app2 });

// Now we can login with the token. Further calls to `accounts2` will be authenticated
accounts2.loginWithToken(token);

You can see a proof of concept of this architecture in an example repository.

28
Migrating to Meteor 1.4

Breaking changes
These are all the breaking changes — that is, changes you absolutely have to worry about if
you are updating your app from 1.3.x to 1.4. However, we recommend that you also consider
the recommended changes listed below.

Binary Packages require a Build Toolchain


The headline feature of Meteor 1.4 is the upgrade to Node version 4.4.7. Node 4 includes a
changed ABI (application binary interface), which means that binary npm packages that your
application uses will need to be recompiled.

Some very common binary packages (such as npm-bcrypt ) will already have been
republished for the Node 4 platform, so if you are using a limited set of packages, this may
not affect you; however if you are using less common dependencies, this may be an issue.

If you have binary npm packages in your application node_modules directory, you should run
meteor npm rebuild (after meteor update ) in your application directory to recompile those

packages.

Meteor will automatically recompile any binary npm dependencies of Meteor packages, if
they were not already compiled with the correct ABI. This will typically happen the first time
you start your application after updating to 1.4, but it may also happen when you meteor add
some:package that was published using a different version of Meteor and/or Node.

In order for this rebuilding to work, you will need to install a basic compiler toolchain on your
development machine. Specifically,

OS X users should install the commandline tools (in short, run xcode-select --
install ).

Windows users should install the MS Build Tools.

Linux users should ensure they have Python 2.7, make and a C compiler (g++)
installed.

To test that your compiler toolchain is installed and working properly, try installing any binary
npm package in your application using meteor npm . For example, run meteor npm install
bcrypt then meteor node , then try calling require("bcrypt") from the Node shell.

Update from MongoDB 2.4

29
Migrating to Meteor 1.4

Meteor has been updated to use version 2.2.4 of the node MongoDB driver. This means
Meteor now ships with full support for MongoDB 3.2 (the latest stable version) and the
WiredTiger storage engine. We recommend you update your application to MongoDB 3.2.

If you are currently using MongoDB 2.4, please note that the version has reached end-of-life
and you should at the least update to version 2.6. Version 2.6 is the minimum version
supported by Meteor 1.4.

Updating your database to 2.6 is generally pretty painless. Please consult the MongoDB
documentation for details about how to do so.

As of 1.4, you must ensure your MONGO_OPLOG_URL contains a replicaSet argument


(see the changelog and the oplog documentation).

NOTE: Some MongoDB hosting providers may have a deployment setup that doesn't
require you to use a replicaSet argument. For example, Compose.io has two types of
deployments, MongoDB Classic and MongoDB+. The new MongoDB+ offering is a
sharded setup and not a true replica set (despite the shard being implemented as a
replica set) so it does not require the replicaSet parameter and Meteor will throw an
error if you add it to your connection strings.

If you see a failed authentication you may need to upgrade to SCRAM-SHA-1,


essentially: use admin, db.adminCommand({authSchemaUpgrade: 1}); . You may need to
delete and re-add your oplog reader user.

Remove debugger statements


Due to changes in Node 4, if you have `debugger` statements in your code they will now hit
the breakpoint even without a debugger attached. This also means you can now debug
without using the `--debug-brk` option.

Password reset and enrollment tokens now expire


Password Reset tokens now expire (after 3 days by default -- can be modified via
Accounts.config({ passwordResetTokenExpirationInDays: ...}). [PR #7534]
(https://github.com/meteor/meteor/pull/7534) See [PR #7794]
(https://github.com/meteor/meteor/issues/7794) for infomation about splitting reset vs
enrollment tokens and allowing different expiration times.

Recommendations
Update to Meteor 1.3.5.1 first

30
Migrating to Meteor 1.4

Though not mandatory, it may be helpful to update your apps to Meteor 1.3.5.1 before
updating to 1.4, since 1.3.5.1 is the most recent release before 1.4, and contains much of
the same code as 1.4. To update an app to 1.3.5.1, run meteor update --release 1.3.5.1 in
the app directory. When you are confident the app is working correctly, meteor update will
take you all the way to Meteor 1.4.

Update to MongoDB 3.2


Although Meteor 1.4 supports MongoDB 2.6 and up, as well as the older MMAPv1 storage
engine, we recommend you update your database to use the new WiredTiger storage
engine and use MongoDB 3.2.

To update your production database to version 3.2 you should follow the steps listed in the
MongoDB documentation. To update your storage engine, you should ensure you follow the
"Change Storage Engine to WiredTiger" instructions in the 3.0 upgrade documentation.

If you are using OS X or 64bit Linux, you can update your development database in a similar
way (if you are running meteor as usual, you can connect to the development database at
localhost:3001/meteor ). However, if you are not concerned about the data in your

development database, the easiest thing to do is to remove all local data (including your
development database) with meteor reset . When you next start meteor , the database will
be recreated with a 3.2 WiredTiger engine.

If you are using Windows or 32bit Linux, you can update your development database to 3.2,
however it will continue to use the MMAPv1 storage engine, as the 32bit MongoDB binary
does not support WiredTiger.

Use Nested Imports


Thanks to the use of the reify library, Meteor now fully supports nested import declarations
in both application and package modules, whereas previously they were only allowed in
application code:

if (Meteor.isClient) {
import { symbol } from './client-only/file';
}

One place this is particularly useful is in test files that are only intended to run on the client
or the server — you can now use import wherever you like, without having to organize
your tests in client or server directories.

31
Migrating to Meteor 1.4

32
Migrating to Meteor 1.3

Breaking changes
These are all the breaking changes -- that is changes that you absolutely have to worry
about if you are updating your app from 1.2.x to 1.3. However, we recommend that you also
consider the recommended changes listed in the other sections below.

Files in a directory named imports/ will no longer load eagerly. (You should probably
rename such a directory as it the basis of our new module system).

Files within your app named *.test[s].* , *.app-test[s].* , *.spec[s].* and *.app-
spec[s].* will no longer load eagerly (you should probably rename such a file if it

doesn't contain tests, as it will be eagerly loaded by our new app testing modes).

If you are using React you will now need to install a set of React npm packages in your
app. See the recommendations for React below for more details.

Mobile
iOS apps now require iOS 8 or higher, and building for iOS requires Xcode 7.2 or higher
to be installed.

Building for Android now requires Android SDK 23 to be installed. You may also need to
create a new AVD for the emulator.

Cordova has been upgraded to the most recent versions (Cordova 6.0.0, Cordova iOS
4.0.1 and Cordova Android 5.1.0). This may require you to upgrade your plugin
versions. We pin core Cordova plugins to versions known to be compatible and warn
about this during build, but you may encounter compile time or runtime errors with third
party plugins. Upgrading to newer versions of these plugins may help if they have been
updated to work with recent versions of Cordova.

The plugin used to serve your app's files and support hot code push has been
completely rewritten. As a result, files are now served from localhost instead of
meteor.local , with a fixed port number derived from your appId . You may have to

update OAuth redirect URLs to point to the new local domain and port.

Recommendations: modules
The biggest new feature in Meteor 1.3 is support for ES2015 modules on the client and the
server. Using modules you can declare dependencies between files, control load order, and
use npm packages on the client and server easily.

33
Migrating to Meteor 1.3

You should load all Meteor "pseudo-globals" using the `import { Name } from
'meteor/package' syntax. For instance:

import { Meteor } from 'meteor/meteor';


import { EJSON } from 'meteor/ejson';

You should consider installing the meteor-node-stubs npm package to allow using npm
packages written for node on the browser:

meteor npm install --save meteor-node-stubs

If you are using app-local packages to control load order and write unit tests for your
application, we recommend you switch to using modules:
Remove code related to the Package API from the package.js files and rename
them to index.js ,
Move your local packages to the imports/ directory.
Add the necessary import statements to each of the modules in your packages.
Add export statements to each of your packages exports.

api.addFiles('file.js');
// For files that are not imported elsewhere, this turns into
import './file.js';

// Remove from package.js


api.export('Foo');

// localPackage/foo.js
// Foo must be explicitly exported
export default Foo;

// client/main.js
import '/imports/localPackage';

You can read about our recommended structure for applications and modules in the
Application Structure article of the Meteor Guide, and how to test them in the Testing
article.

If you are using Atmosphere packages which simply wrap npm packages, both on the
client and server, it is now recommended that you simply install them using npm. Run
npm init to initialize your package.json and install packages with npm install --save

(or npm install --save-dev if it's development dependency for testing etc.). We have
some tips about how to use npm packages written in an asynchronous style.

34
Migrating to Meteor 1.3

Also, you should no longer need to use the meteorhacks:npm package. To migrate, follow the
following steps:

1. Remove packages from your app: meteor remove meteorhacks:npm npm-container .


2. Remove the generated npm-container package: rm -r packages/npm-container .
3. Move the contents of packages.json to the dependencies section of your
package.json (you may need to create one with meteor npm init ).

4. Simply use import instead of Npm.require() .

Recommendations: package authors


Package authors are recommended to:

No longer publish wrapper packages that do no more than include an npm package /
client side lib. If your package adds significant wrappers around the npm package, it
might make sense however.

Publish to npm when appropriate, especially if your package can be used by the wider
JS community!

Use api.mainModule() and export from your main module rather than api.exports()
in Atmosphere packages.

If you depend (directly or transitively) on a client side npm package that is large or
problematic if installed twice (e.g. React), use tmeasday:check-npm-versions to declare
"peer" dependencies. If the client side npm package you depend on is angular , you
can support both Meteor 1.2 and 1.3 using this solution. Read more about this in the
Writing Packages article.

Recommendations: Testing
Meteor 1.3 includes a new command meteor test , which can be used to run tests against
your app, in two modalities. You can read about these features in much more detail in the
Testing Guide Article.

Full app testing


If you were previously using Velocity to run tests against your running Meteor app, the full
app test mode should allow you to run your tests against 1.3, with some small changes.

35
Migrating to Meteor 1.3

To convert tests, you'll need to change or upgrade your test driver package to a 1.3
compatible package (as of this writing there is only one choice practicalmeteor:mocha
but we expect more to exist in the future). You should name your test files in the pattern
*.app-test[s].* and place them outside of tests/ directories. To run the tests you

can run meteor test --full-app --driver-package <driver-package>

Note that full app test mode does not run the test reporter in a separate application to
the app under test, and does not amalgamate results from multiple testing systems, as
Velocity does. This effectively means if you are using more than one testing system, you
will need to run meteor test --full-app multiple times.

Also, it means certain types of tests are better off written as acceptance tests outside of
the Meteor tool.

Module testing
If you were previously using in-app packages in order to unit test your app, you should
switch to a modules-based approach and test them using the normal test mode.

To convert your unit tests to run against the app, first upgrade your test driver (see
above) and then place your test files alongside the modules they are testing with a
name matching *.tests.* . Such files will automatically be added to your "test app"
when you run meteor test --driver-package <driver-package> . You can import the
modules that you need to test against within each test file.

Some example tests can be seen the Todos example app

Recommendations: Mobile
Alongside some of the breaking mobile changes listed above, there are some changes in the
way the mobile integration works that you should consider:

Some low resolution app icon and launch images sizes for now unsupported devices
have been deprecated. To avoid a deprecation warning during build, please remove the
entries from your mobile-config.js . (You probably also want to remove the image files
from your project.)

The plugin now allows for local file access on both iOS and Android. You can construct
file system URLs manually ( http://localhost:<port>/local-filesystem/<path> ) or use
WebAppLocalServer.localFileSystemUrl() to convert a file:// URL.

Install React from npm


36
Migrating to Meteor 1.3

In Meteor 1.3, we recommend installing react and react-dom into your app using npm,
and importing them from your app code:

import React from 'react';


import ReactDOM from 'react-dom';

As mentioned in the breaking changes, the react Atmosphere package still works, but it
now expects you to install the React npm packages it uses in your application (read the
Using Packages article for more details about how to manage your npm dependencies):

npm install --save react react-dom react-addons-transition-group \


react-addons-css-transition-group react-addons-linked-state-mixin \
react-addons-create-fragment react-addons-update react-addons-pure-render-mixin \
react-addons-test-utils react-addons-perf

However, we recommend that you should stop using the react or react-runtime
Atmosphere packages and instead install React directly from npm (for more detail, see the
React article of the guide). To make this change in an existing app, you can run:

meteor remove react

# if you are using our data integration


meteor add react-meteor-data

npm install --save react react-dom react-addons-pure-render-mixin

Then, in your application, you should import React directly rather than relying on a global
React symbol:

import React from 'react';

If you are using a package that depends on the react or react-runtime Atmosphere
packages, you will still need to install the full list of npm React packages above, so we
encourage package authors to update their packages to import React directly from npm.

Loading data with React


The react-meteor-data has a new createContainer syntax for combining Meteor's data
system with React in an idiomatic way. We encourage you to use containers to separate
your data loading concerns from your presentational components!

37
Migrating to Meteor 1.3

Install Angular from npm


With an Angular Meteor app you can safely update to Meteor 1.3 without any changes to
your code. You just need to make sure you are using the latest angular Atmosphere
package 1.3.9_2 .

But, in Meteor 1.3, we recommend installing angular and angular-meteor into your app
using npm:

npm install --save angular angular-meteor

and importing them from your app code:

import angular from 'angular';


import angular-meteor from 'angular-meteor';

Read the Using Packages article for more details about how to manage your npm
dependencies.

If you already using the Atmosphere packages and want to move to the npm packages, you
will need to remove the Atmosphere packages first but keep the angular-templates
Atmosphere package:

meteor remove angular


meteor add angular-templates

npm install --save angular angular-meteor

Then, in your application, you should import angular directly rather than relying on global
angular:

import angular from 'angular';


import angular-meteor from 'angular-meteor';

Existing Angular Atmosphere packages


If you are a package author that depends on the angular:angular Atmosphere package,
you can support both Meteor 1.2 and 1.3 so your users will have an easy and unbreaking
update process:

Change your angular:angular dependency into a weak dependency:

38
Migrating to Meteor 1.3

api.use('angular:angular@1.5.3', 'client', { weak: true });

and then add a dependency check for both Meteor 1.2 and 1.3 before initializing your
angular module:

if (!window.angular) {
try {
if (Package['modules-runtime']) {
var require = Package['modules-runtime'].meteorInstall();
require('angular');
}
} catch(e) {
throw new Error('angular package is missing');
}
}

angular.module('your.module', []);

New guide articles


As part of the 1.3 release, we have some new guide articles and updated sections of
existing articles:

There's a Application Structure article which explains how to structure your files and use
the module system.

There's a Code Style article that makes recommendations about how to ensure
consistent formatting for your code.

There's a Testing article which covers how to do various kinds of testing in Meteor.

There's a React article which explains how to best use React with Meteor

There's a Mobile article which covers how to best use our Cordova integration.

There's a Using Packages article which explains how best to use both npm and
Atmosphere packages in your app.

There's a Writing Packages article which explains practice for writing Atmosphere
packages and using all kinds of dependencies within them.

The UI/UX article has been updated to explain how to do i18n in Meteor applications.

39
Migrating to Meteor 1.3

40
Collections and Schemas

After reading this guide, you'll know:

1. The different types of MongoDB collections in Meteor, and how to use them.
2. How to define a schema for a collection to control its content.
3. What to consider when defining your collection's schema.
4. How to enforce the schema when writing to a collection.
5. How to carefully change the schema of your collection.
6. How to deal with associations between records.

MongoDB collections in Meteor


At its core, a web application offers its users a view into, and a way to modify, a persistent
set of data. Whether managing a list of todos, or ordering a car to pick you up, you are
interacting with a permanent but constantly changing data layer.

In Meteor, that data layer is typically stored in MongoDB. A set of related data in MongoDB is
referred to as a "collection". In Meteor you access MongoDB through collections, making
them the primary persistence mechanism for your app data.

However, collections are a lot more than a way to save and retrieve data. They also provide
the core of the interactive, connected user experience that users expect from the best
applications. Meteor makes this user experience easy to implement.

In this article, we'll look closely at how collections work in various places in the framework,
and how to get the most out of them.

Server-side collections
When you create a collection on the server:

Todos = new Mongo.Collection('Todos');

You are creating a collection within MongoDB, and an interface to that collection to be used
on the server. It's a fairly straightforward layer on top of the underlying Node MongoDB
driver, but with a synchronous API:

// This line won't complete until the insert is done


Todos.insert({_id: 'my-todo'});
// So this line will return something
const todo = Todos.findOne({_id: 'my-todo'});
// Look ma, no callbacks!
console.log(todo);

41
Collections and Schemas

Client-side collections
On the client, when you write the same line:

Todos = new Mongo.Collection('Todos');

It does something totally different!

On the client, there is no direct connection to the MongoDB database, and in fact a
synchronous API to it is not possible (nor probably what you want). Instead, on the client, a
collection is a client side cache of the database. This is achieved thanks to the Minimongo
library---an in-memory, all JS, implementation of the MongoDB API. What this means is that
on the client, when you write:

// This line is changing an in-memory Minimongo data structure


Todos.insert({_id: 'my-todo'});
// And this line is querying it
const todo = Todos.findOne({_id: 'my-todo'});
// So this happens right away!
console.log(todo);

The way that you move data from the server (and MongoDB-backed) collection into the
client (in-memory) collection is the subject of the data loading article. Generally speaking,
you subscribe to a publication, which pushes data from the server to the client. Usually, you
can assume that the client contains an up-to-date copy of some subset of the full MongoDB
collection.

To write data back to the server, you use a Method, the subject of the methods article.

Local collections
There is a third way to use a collection in Meteor. On the client or server, if you create a
collection but pass null instead of a name:

SelectedTodos = new Mongo.Collection(null);

This creates a local collection. This is a Minimongo collection that has no database
connection (ordinarily a named collection would either be directly connected to the database
on the server, or via a subscription on the client).

A local collection is a convenient way to use the full power of the Minimongo library for in-
memory storage. For instance, you might use it instead of a simple array if you need to
execute complex queries over your data. Or you may want to take advantage of its reactivity

42
Collections and Schemas

on the client to drive some UI in a way that feels natural in Meteor.

Defining a schema
Although MongoDB is a schema-less database, which allows maximum flexibility in data
structuring, it is generally good practice to use a schema to constrain the contents of your
collection to conform to a known format. If you don't, then you tend to end up needing to
write defensive code to check and confirm the structure of your data as it comes out of the
database, instead of when it goes into the database. As in most things, you tend to read
data more often than you write it, and so it's usually easier, and less buggy to use a schema
when writing.

In Meteor, the pre-eminent schema package is aldeed:simple-schema. It's an expressive,


MongoDB based schema that's used to insert and update documents. Another alternative is
jagi:astronomy which is a full Object Model (OM) layer offering schema definition,
server/client side validators, object methods and event handlers.

Let's assume that we have a Lists collection. To define a schema for this collection using
simple-schema , you can simply create a new instance of the SimpleSchema class and attach

it to the Lists object:

Lists.schema = new SimpleSchema({


name: {type: String},
incompleteCount: {type: Number, defaultValue: 0},
userId: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}
});

This example from the Todos app defines a schema with a few simple rules:

1. We specify that the name field of a list is required and must be a string.
2. We specify the incompleteCount is a number, which on insertion is set to 0 if not
otherwise specified.
3. We specify that the userId , which is optional, must be a string that looks like the ID of
a user document.

We attach the schema to the namespace of Lists directly, which allows us to check
objects against this schema directly whenever we want, such as in a form or Method. In the
next section we'll see how to use this schema automatically when writing to the collection.

You can see that with relatively little code we've managed to restrict the format of a list
significantly. You can read more about more complex things that can be done with schemas
in the Simple Schema docs.

43
Collections and Schemas

Validating against a schema


Now we have a schema, how do we use it?

It's pretty straightforward to validate a document with a schema. We can write:

const list = {
name: 'My list',
incompleteCount: 3
};

Lists.schema.validate(list);

In this case, as the list is valid according to the schema, the validate() line will run without
problems. If however, we wrote:

const list = {
name: 'My list',
incompleteCount: 3,
madeUpField: 'this should not be here'
};

Lists.schema.validate(list);

Then the validate() call will throw a ValidationError which contains details about what is
wrong with the list document.

The `ValidationError`
What is a ValidationError ? It's a special error that is used in Meteor to indicate a user-
input based error in modifying a collection. Typically, the details on a ValidationError are
used to mark up a form with information about what inputs don't match the schema. In the
methods article, we'll see more about how this works.

Designing your data schema


Now that you are familiar with the basic API of Simple Schema, it's worth considering a few
of the constraints of the Meteor data system that can influence the design of your data
schema. Although generally speaking you can build a Meteor data schema much like any
MongoDB data schema, there are some important details to keep in mind.

44
Collections and Schemas

The most important consideration is related to the way DDP, Meteor's data loading protocol,
communicates documents over the wire. The key thing to realize is that DDP sends changes
to documents at the level of top-level document fields. What this means is that if you have
large and complex subfields on document that change often, DDP can send unnecessary
changes over the wire.

For instance, in "pure" MongoDB you might design the schema so that each list document
had a field called todos which was an array of todo items:

Lists.schema = new SimpleSchema({


name: {type: String},
todos: {type: [Object]}
});

The issue with this schema is that due to the DDP behavior just mentioned, each change to
any todo item in a list will require sending the entire set of todos for that list over the network.
This is because DDP has no concept of "change the text field of the 3rd item in the field
called todos ", simply "change the field called todos to a totally new array".

Denormalization and multiple collections


The implication of the above is that we need to create more collections to contain sub-
documents. In the case of the Todos application, we need both a Lists collection and a
Todos collection to contain each list's todo items. Consequently we need to do some things

that you'd typically associate with a SQL database, like using foreign keys ( todo.listId ) to
associate one document with another.

In Meteor, it's often less of a problem doing this than it would be in a typical MongoDB
application, as it's easy to publish overlapping sets of documents (we might need one set of
users to render one screen of our app, and an intersecting set for another), which may stay
on the client as we move around the application. So in that scenario there is an advantage to
separating the subdocuments from the parent.

However, given that MongoDB prior to version 3.2 doesn't support queries over multiple
collections ("joins"), we typically end up having to denormalize some data back onto the
parent collection. Denormalization is the practice of storing the same piece of information in
the database multiple times (as opposed to a non-redundant "normal" form). MongoDB is a
database where denormalizing is encouraged, and thus optimized for this practice.

In the case of the Todos application, as we want to display the number of unfinished todos
next to each list, we need to denormalize list.incompleteTodoCount . This is an
inconvenience but typically reasonably easy to do as we'll see in the section on abstracting
denormalizers below.

45
Collections and Schemas

Another denormalization that this architecture sometimes requires can be from the parent
document onto sub-documents. For instance, in Todos, as we enforce privacy of the todo
lists via the list.userId attribute, but we publish the todos separately, it might make sense
to denormalize todo.userId also. To do this, we'd need to be careful to take the userId
from the list when creating the todo, and updating all relevant todos whenever a list's
userId changed.

Designing for the future


An application, especially a web application, is rarely finished, and it's useful to consider
potential future changes when designing your data schema. As in most things, it's rarely a
good idea to add fields before you actually need them (often what you anticipate doesn't
actually end up happening, after all).

However, it's a good idea to think ahead to how the schema may change over time. For
instance, you may have a list of strings on a document (perhaps a set of tags). Although it's
tempting to leave them as a subfield on the document (assuming they don't change much), if
there's a good chance that they'll end up becoming more complicated in the future (perhaps
tags will have a creator, or subtags later on?), then it might be easier in the long run to make
a separate collection from the beginning.

The amount of foresight you bake into your schema design will depend on your app's
individual constraints, and will need to be a judgement call on your part.

Using schemas on write


Although there are a variety of ways that you can run data through a Simple Schema before
sending it to your collection (for instance you could check a schema in every method call),
the simplest and most reliable is to use the aldeed:collection2 package to run every
mutator ( insert/update/upsert call) through the schema.

To do so, we use attachSchema() :

Lists.attachSchema(Lists.schema);

What this means is that now every time we call Lists.insert() , Lists.update() ,
Lists.upsert() , first our document or modifier will be automatically checked against the

schema (in subtly different ways depending on the exact mutator).

`defaultValue` and data cleaning

46
Collections and Schemas

One thing that Collection2 does is "clean" the data before sending it to the database. This
includes but is not limited to:

1. Coercing types - converting strings to numbers


2. Removing attributes not in the schema
3. Assigning default values based on the defaultValue in the schema definition

However, sometimes it's useful to do more complex initialization to documents before


inserting them into collections. For instance, in the Todos app, we want to set the name of
new lists to be List X where X is the next available unique letter.

To do so, we can subclass Mongo.Collection and write our own insert() method:

class ListsCollection extends Mongo.Collection {


insert(list, callback) {
if (!list.name) {
let nextLetter = 'A';
list.name = `List ${nextLetter}`;

while (!!this.findOne({name: list.name})) {


// not going to be too smart here, can go past Z
nextLetter = String.fromCharCode(nextLetter.charCodeAt(0) + 1);
list.name = `List ${nextLetter}`;
}
}

// Call the original `insert` method, which will validate


// against the schema
return super.insert(list, callback);
}
}

Lists = new ListsCollection('Lists');

Hooks on insert/update/remove
The technique above can also be used to provide a location to "hook" extra functionality into
the collection. For instance, when removing a list, we always want to remove all of its todos
at the same time.

We can use a subclass for this case as well, overriding the remove() method:

47
Collections and Schemas

class ListsCollection extends Mongo.Collection {


// ...
remove(selector, callback) {
Package.todos.Todos.remove({listId: selector});
return super.remove(selector, callback);
}
}

This technique has a few disadvantages:

1. Mutators can get very long when you want to hook in multiple times.
2. Sometimes a single piece of functionality can be spread over multiple mutators.
3. It can be a challenge to write a hook in a completely general way (that covers every
possible selector and modifier), and it may not be necessary for your application
(because perhaps you only ever call that mutator in one way).

A way to deal with points 1. and 2. is to separate out the set of hooks into their own module,
and simply use the mutator as a point to call out to that module in a sensible way. We'll see
an example of that below.

Point 3. can usually be resolved by placing the hook in the Method that calls the mutator,
rather than the hook itself. Although this is an imperfect compromise (as we need to be
careful if we ever add another Method that calls that mutator in the future), it is better than
writing a bunch of code that is never actually called (which is guaranteed to not work!), or
giving the impression that your hook is more general that it actually is.

Abstracting denormalizers
Denormalization may need to happen on various mutators of several collections. Therefore,
it's sensible to define the denormalization logic in one place, and hook it into each mutator
with one line of code. The advantage of this approach is that the denormalization logic is one
place rather than spread over many files, but you can still examine the code for each
collection and fully understand what happens on each update.

In the Todos example app, we build a incompleteCountDenormalizer to abstract the counting


of incomplete todos on the lists. This code needs to run whenever a todo item is inserted,
updated (checked or unchecked), or removed. The code looks like:

48
Collections and Schemas

const incompleteCountDenormalizer = {
_updateList(listId) {
// Recalculate the correct incomplete count direct from MongoDB
const incompleteCount = Todos.find({
listId,
checked: false
}).count();

Lists.update(listId, {$set: {incompleteCount}});


},
afterInsertTodo(todo) {
this._updateList(todo.listId);
},
afterUpdateTodo(selector, modifier) {
// We only support very limited operations on todos
check(modifier, {$set: Object});

// We can only deal with $set modifiers, but that's all we do in this app
if (_.has(modifier.$set, 'checked')) {
Todos.find(selector, {fields: {listId: 1}}).forEach(todo => {
this._updateList(todo.listId);
});
}
},
// Here we need to take the list of todos being removed, selected *before* the update

// because otherwise we can't figure out the relevant list id(s) (if the todo has be
en deleted)
afterRemoveTodos(todos) {
todos.forEach(todo => this._updateList(todo.listId));
}
};

We are then able to wire in the denormalizer into the mutations of the Todos collection like
so:

class TodosCollection extends Mongo.Collection {


insert(doc, callback) {
doc.createdAt = doc.createdAt || new Date();
const result = super.insert(doc, callback);
incompleteCountDenormalizer.afterInsertTodo(doc);
return result;
}
}

Note that we only handled the mutators we actually use in the application---we don't deal
with all possible ways the todo count on a list could change. For example, if you changed the
listId on a todo item, it would need to change the incompleteCount of two lists. However,

49
Collections and Schemas

since our application doesn't do this, we don't handle it in the denormalizer.

Dealing with every possible MongoDB operator is difficult to get right, as MongoDB has a
rich modifier language. Instead we focus on just dealing with the modifiers we know we'll see
in our app. If this gets too tricky, then moving the hooks for the logic into the Methods that
actually make the relevant modifications could be sensible (although you need to be diligent
to ensure you do it in all the relevant places, both now and as the app changes in the future).

It could make sense for packages to exist to completely abstract some common
denormalization techniques and actually attempt to deal with all possible modifications. If
you write such a package, please let us know!

Migrating to a new schema


As we discussed above, trying to predict all future requirements of your data schema ahead
of time is impossible. Inevitably, as a project matures, there will come a time when you need
to change the schema of the database. You need to be careful about how you make the
migration to the new schema to make sure your app works smoothly during and after the
migration.

Writing migrations
A useful package for writing migrations is percolate:migrations , which provides a nice
framework for switching between different versions of your schema.

Suppose, as an example, that we wanted to add a list.todoCount field, and ensure that it
was set for all existing lists. Then we might write the following in server-only code (e.g.
/server/migrations.js ):

Migrations.add({
version: 1,
up() {
Lists.find({todoCount: {$exists: false}}).forEach(list => {
const todoCount = Todos.find({listId: list._id}).count();
Lists.update(list._id, {$set: {todoCount}});
});
},
down() {
Lists.update({}, {$unset: {todoCount: true}}, {multi: true});
}
});

50
Collections and Schemas

This migration, which is sequenced to be the first migration to run over the database, will,
when called, bring each list up to date with the current todo count.

To find out more about the API of the Migrations package, refer to its documentation.

Bulk changes
If your migration needs to change a lot of data, and especially if you need to stop your app
server while it's running, it may be a good idea to use a MongoDB Bulk Operation.

The advantage of a bulk operation is that it only requires a single round trip to MongoDB for
the write, which usually means it is a lot faster. The downside is that if your migration is
complex (which it usually is if you can't just do an .update(.., .., {multi: true}) ), it can
take a significant amount of time to prepare the bulk update.

What this means is if users are accessing the site whilst the update is being prepared, it will
likely go out of date! Also, a bulk update will lock the entire collection while it is being
applied, which can cause a significant blip in your user experience if it takes a while. For
these reason, you often need to stop your server and let your users know you are
performing maintenance while the update is happening.

We could write our above migration like so (note that you must be on MongoDB 2.6 or later
for the bulk update operations to exist). We can access the native MongoDB API via
Collection#rawCollection() :

51
Collections and Schemas

Migrations.add({
version: 1,
up() {
// This is how to get access to the raw MongoDB node collection that the Meteor se
rver collection wraps
const batch = Lists.rawCollection().initializeUnorderedBulkOp();

//Mongo throws an error if we execute a batch operation without actual operations,


e.g. when Lists was empty.
let hasUpdates = false;
Lists.find({todoCount: {$exists: false}}).forEach(list => {
const todoCount = Todos.find({listId: list._id}).count();
// We have to use pure MongoDB syntax here, thus the `{_id: X}`
batch.find({_id: list._id}).updateOne({$set: {todoCount}});
hasUpdates = true;
});

if(hasUpdates){
// We need to wrap the async function to get a synchronous API that migrations e
xpects
const execute = Meteor.wrapAsync(batch.execute, batch);
return execute();
}

return true;
},
down() {
Lists.update({}, {$unset: {todoCount: true}}, {multi: true});
}
});

Note that we could make this migration faster by using an Aggregation to gather the initial
set of todo counts.

Running migrations
To run a migration against your development database, it's easiest to use the Meteor shell:

// After running `meteor shell` on the command line:


Migrations.migrateTo('latest');

If the migration logs anything to the console, you'll see it in the terminal window that is
running the Meteor server.

To run a migration against your production database, run your app locally in production
mode (with production settings and environment variables, including database settings), and
use the Meteor shell in the same way. What this does is run the up() function of all

52
Collections and Schemas

outstanding migrations, against your production database. In our case, it should ensure all
lists have a todoCount field set.

A good way to do the above is to spin up a virtual machine close to your database that has
Meteor installed and SSH access (a special EC2 instance that you start and stop for the
purpose is a reasonable option), and running the command after shelling into it. That way
any latencies between your machine and the database will be eliminated, but you still can be
very careful about how the migration is run.

Note that you should always take a database backup before running any migration!

Breaking schema changes


Sometimes when we change the schema of an application, we do so in a breaking way -- so
that the old schema doesn't work properly with the new code base. For instance, if we had
some UI code that heavily relied on all lists having a todoCount set, there would be a
period, before the migration runs, in which the UI of our app would be broken after we
deployed.

The simple way to work around the problem is to take the application down for the period in
between deployment and completing the migration. This is far from ideal, especially
considering some migrations can take hours to run (although using Bulk Updates probably
helps a lot here).

A better approach is a multi-stage deployment. The basic idea is that:

1. Deploy a version of your application that can handle both the old and the new schema.
In our case, it'd be code that doesn't expect the todoCount to be there, but which
correctly updates it when new todos are created.
2. Run the migration. At this point you should be confident that all lists have a todoCount .
3. Deploy the new code that relies on the new schema and no longer knows how to deal
with the old schema. Now we are safe to rely on list.todoCount in our UI.

Another thing to be aware of, especially with such multi-stage deploys, is that being
prepared to rollback is important! For this reason, the migrations package allows you to
specify a down() function and call Migrations.migrateTo(x) to migrate back to version x .

So if we wanted to reverse our migration above, we'd run

// The "0" migration is the unmigrated (before the first migration) state
Migrations.migrateTo(0);

If you find you need to roll your code version back, you'll need to be careful about the data,
and step carefully through your deployment steps in reverse.

53
Collections and Schemas

Caveats
Some aspects of the migration strategy outlined above are possibly not the most ideal way
to do things (although perhaps appropriate in many situations). Here are some other things
to be aware of:

1. Usually it is better to not rely on your application code in migrations (because the
application will change over time, and the migrations should not). For instance, having
your migrations pass through your Collection2 collections (and thus check schemas, set
autovalues etc) is likely to break them over time as your schemas change over time.

One way to avoid this problem is simply to not run old migrations on your database.
This is a little bit limiting but can be made to work.

2. Running the migration on your local machine will probably make it take a lot longer as
your machine isn't as close to the production database as it could be.

Deploying a special "migration application" to the same hardware as your real application is
probably the best way to solve the above issues. It'd be amazing if such an application kept
track of which migrations ran when, with logs and provided a UI to examine and run them.
Perhaps a boilerplate application to do so could be built (if you do so, please let us know and
we'll link to it here!).

Associations between collections


As we discussed earlier, it's very common in Meteor applications to have associations
between documents in different collections. Consequently, it's also very common to need to
write queries fetching related documents once you have a document you are interested in
(for instance all the todos that are in a single list).

To make this easier, we can attach functions to the prototype of the documents that belong
to a given collection, to give us "methods" on the documents (in the object oriented sense).
We can then use these methods to create new queries to find related documents.

Collection helpers
We can use the dburles:collection-helpers package to easily attach such methods (or
"helpers") to documents. For instance:

54
Collections and Schemas

Lists.helpers({
// A list is considered to be private if it has a userId set
isPrivate() {
return !!this.userId;
}
});

Once we've attached this helper to the Lists collection, every time we fetch a list from the
database (on the client or server), it will have a .isPrivate() function available:

const list = Lists.findOne();


if (list.isPrivate()) {
console.log('The first list is private!');
}

Association helpers
Now we can attach helpers to documents, it's simple to define a helper that fetches related
documents

Lists.helpers({
todos() {
return Todos.find({listId: this._id}, {sort: {createdAt: -1}});
}
});

Now we can easily find all the todos for a list:

const list = Lists.findOne();


console.log(`The first list has ${list.todos().count()} todos`);

55
Publications and Data Loading

After reading this guide, you'll know:

1. What publications and subscriptions are in the Meteor platform.


2. How to define a publication on the server.
3. Where to subscribe on the client and in which templates.
4. Useful patterns for managing subscriptions.
5. How to reactively publish related data.
6. How to ensure your publication is secure in the face of reactive changes.
7. How to use the low-level publish API to publish anything.
8. What happens when you subscribe to a publication.
9. How to turn a 3rd-party REST endpoint into a publication.
10. How to turn a publication in your app into a REST endpoint.

Publications and subscriptions


In a traditional, HTTP-based web application, the client and server communicate in a
"request-response" fashion. Typically the client makes RESTful HTTP requests to the server
and receives HTML or JSON data in response, and there's no way for the server to "push"
data to the client when changes happen at the backend.

Meteor is built from the ground up on the Distributed Data Protocol (DDP) to allow data
transfer in both directions. Building a Meteor app doesn't require you to set up REST
endpoints to serialize and send data. Instead you create publication endpoints that can push
data from server to client.

In Meteor a publication is a named API on the server that constructs a set of data to send
to a client. A client initiates a subscription which connects to a publication, and receives
that data. That data consists of a first batch sent when the subscription is initialized and then
incremental updates as the published data changes.

So a subscription can be thought of as a set of data that changes over time. Typically, the
result of this is that a subscription "bridges" a server-side MongoDB collection, and the client
side Minimongo cache of that collection. You can think of a subscription as a pipe that
connects a subset of the "real" collection with the client's version, and constantly keeps it up
to date with the latest information on the server.

Defining a publication
A publication should be defined in a server-only file. For instance, in the Todos example app,
we want to publish the set of public lists to all users:

56
Publications and Data Loading

Meteor.publish('lists.public', function() {
return Lists.find({
userId: {$exists: false}
}, {
fields: Lists.publicFields
});
});

There are a few things to understand about this code block. First, we've named the
publication with the unique string lists.public , and that will be how we access it from the
client. Second, we are simply returning a Mongo cursor from the publication function. Note
that the cursor is filtered to only return certain fields from the collection, as detailed in the
Security article.

What that means is that the publication will simply ensure the set of data matching that
query is available to any client that subscribes to it. In this case, all lists that do not have a
userId setting. So the collection named Lists on the client will have all of the public lists

that are available in the server collection named Lists while that subscription is open. In
this particular example in the Todos application, the subscription is initialized when the app
starts and never stopped, but a later section will talk about subscription life cycle.

Every publication takes two types of parameters:

1. The this context, which has information about the current DDP connection. For
example, you can access the current user's _id with this.userId .
2. The arguments to the publication, which can be passed in when calling
Meteor.subscribe .

Note: Since we need to access context on this we need to use the function() {}
form for publications rather than the ES2015 () => {} . You can disable the arrow
function linting rule for publication files with eslint-disable prefer-arrow-callback . A
future version of the publication API will work more nicely with ES2015.

In this publication, which loads private lists, we need to use this.userId to get only the
todo lists that belong to a specific user.

57
Publications and Data Loading

Meteor.publish('lists.private', function() {
if (!this.userId) {
return this.ready();
}

return Lists.find({
userId: this.userId
}, {
fields: Lists.publicFields
});
});

Thanks to the guarantees provided by DDP and Meteor's accounts system, the above
publication can be confident that it will only ever publish private lists to the user that they
belong to. Note that the publication will re-run if the user logs out (or back in again), which
means that the published set of private lists will change as the active user changes.

In the case of a logged-out user, we explicitly call this.ready() , which indicates to the
subscription that we've sent all the data we are initially going to send (in this case none). It's
important to know that if you don't return a cursor from the publication or call this.ready() ,
the user's subscription will never become ready, and they will likely see a loading state
forever.

Here's an example of a publication which takes a named argument. Note that it's important
to check the types of arguments that come in over the network.

Meteor.publish('todos.inList', function(listId) {
// We need to check the `listId` is the type we expect
new SimpleSchema({
listId: {type: String}
}).validate({ listId });

// ...
});

When we subscribe to this publication on the client, we can provide this argument via the
Meteor.subscribe() call:

Meteor.subscribe('todos.inList', list._id);

Organizing publications

58
Publications and Data Loading

It makes sense to place a publication alongside the feature that it's targeted for. For
instance, sometimes publications provide very specific data that's only really useful for the
view for which they were developed. In that case, placing the publication in the same module
or directory as the view code makes perfect sense.

Often, however, a publication is more general. For example in the Todos example
application, we create a todos.inList publication, which publishes all the todos in a list.
Although in the application we only use this in one place (in the Lists_show template), in a
larger app, there's a good chance we might need to access all the todos for a list in other
places. So putting the publication in the todos package is a sensible approach.

Subscribing to data
To use publications, you need to create a subscription to it on the client. To do so, you call
Meteor.subscribe() with the name of the publication. When you do this, it opens up a

subscription to that publication, and the server starts sending data down the wire to ensure
that your client collections contain up to date copies of the data specified by the publication.

Meteor.subscribe() also returns a "subscription handle" with a property called .ready() .

This is a reactive function that returns true when the publication is marked ready (either
you call this.ready() explicitly, or the initial contents of a returned cursor are sent over).

const handle = Meteor.subscribe('lists.public');

Stopping Subscriptions
The subscription handle also has another important property, the .stop() method. When
you are subscribing, it is very important to ensure that you always call .stop() on the
subscription when you are done with it. This ensures that the documents sent by the
subscription are cleared from your local Minimongo cache and the server stops doing the
work required to service your subscription. If you forget to call stop, you'll consume
unnecessary resources both on the client and the server.

However, if you call Meteor.subscribe() conditionally inside a reactive context (such as an


autorun , or getMeteorData in React) or via this.subscribe() in a Blaze component, then

Meteor's reactive system will automatically call this.stop() for you at the appropriate time.

Subscribe in UI components

59
Publications and Data Loading

It is best to place the subscription as close as possible to the place where the data from the
subscription is needed. This reduces "action at a distance" and makes it easier to
understand the flow of data through your application. If the subscription and fetch are
separated, then it's not always clear how and why changes to the subscriptions (such as
changing arguments), will affect the contents of the cursor.

What this means in practice is that you should place your subscription calls in components.
In Blaze, it's best to do this in the onCreated() callback:

Template.Lists_show_page.onCreated(function() {
this.getListId = () => FlowRouter.getParam('_id');

this.autorun(() => {
this.subscribe('todos.inList', this.getListId());
});
});

In this code snippet we can see two important techniques for subscribing in Blaze templates:

1. Calling this.subscribe() (rather than Meteor.subscribe ), which attaches a special


subscriptionsReady() function to the template instance, which is true when all

subscriptions made inside this template are ready.

2. Calling this.autorun sets up a reactive context which will re-initialize the subscription
whenever the reactive function this.getListId() changes.

Read more about Blaze subscriptions in the Blaze article, and about tracking loading state
inside UI components in the UI article.

Fetching data
Subscribing to data puts it in your client-side collection. To use the data in your user
interface, you need to query your client-side collection. There are a couple of important rules
to follow when doing this.

Always use specific queries to fetch data


If you're publishing a subset of your data, it might be tempting to simply query for all data
available in a collection (i.e. Lists.find() ) in order to get that subset on the client, without
re-specifying the Mongo selector you used to publish that data in the first place.

But if you do this, then you open yourself up to problems if another subscription pushes data
into the same collection, since the data returned by Lists.find() might not be what you
expected anymore. In an actively developed application, it's often hard to anticipate what

60
Publications and Data Loading

may change in the future and this can be a source of hard to understand bugs.

Also, when changing between subscriptions, there is a brief period where both subscriptions
are loaded (see Publication behavior when changing arguments below), so when doing
things like pagination, it's exceedingly likely that this will be the case.

Fetch the data nearby where you subscribed to it


We do this for the same reason we subscribe in the component in the first place---to avoid
action at a distance and to make it easier to understand where data comes from. A common
pattern is to fetch the data in a parent template, and then pass it into a "pure" child
component, as we'll see it in the UI Article.

Note that there are some exceptions to this second rule. A common one is Meteor.user() ---
although this is strictly speaking subscribed to (automatically usually), it's typically over-
complicated to pass it through the component hierarchy as an argument to each component.
However keep in mind it's best not to use it in too many places as it makes components
harder to test.

Global subscriptions
One place where you might be tempted to not subscribe inside a component is when it
accesses data that you know you always need. For instance, a subscription to extra fields on
the user object (see the Accounts Article) that you need on every screen of your app.

However, it's generally a good idea to use a layout component (which you wrap all your
components in) to subscribe to this subscription anyway. It's better to be consistent about
such things, and it makes for a more flexible system if you ever decide you have a screen
that doesn't need that data.

Patterns for data loading


Across Meteor applications, there are some common patterns of data loading and
management on the client side that are worth knowing. We'll go into more detail about some
of these in the UI/UX Article.

Subscription readiness
It is key to understand that a subscription will not instantly provide its data. There will be a
latency between subscribing to the data on the client and it arriving from the publication on
the server. You should also be aware that this delay may be a lot longer for your users in

61
Publications and Data Loading

production than for you locally in development!

Although the Tracker system means you often don't need to think too much about this in
building your apps, usually if you want to get the user experience right, you'll need to know
when the data is ready.

To find that out, Meteor.subscribe() and ( this.subscribe() in Blaze components) returns a


"subscription handle", which contains a reactive data source called .ready() :

const handle = Meteor.subscribe('lists.public');


Tracker.autorun(() => {
const isReady = handle.ready();
console.log(`Handle is ${isReady ? 'ready' : 'not ready'}`);
});

We can use this information to be more subtle about when we try and show data to users,
and when we show a loading screen.

Reactively changing subscription arguments


We've already seen an example of using an autorun to re-subscribe when the (reactive)
arguments to a subscription change. It's worth digging in a little more detail to understand
what happens in this scenario.

Template.Lists_show_page.onCreated(function() {
this.getListId = () => FlowRouter.getParam('_id');

this.autorun(() => {
this.subscribe('todos.inList', this.getListId());
});
});

In our example, the autorun will re-run whenever this.getListId() changes, (ultimately
because FlowRouter.getParam('_id') changes), although other common reactive data
sources are:

1. Template data contexts (which you can access reactively with Template.currentData() ).
2. The current user status ( Meteor.user() and Meteor.loggingIn() ).
3. The contents of other application specific client data stores.

Technically, what happens when one of these reactive sources changes is the following:

1. The reactive data source invalidates the autorun computation (marks it so that it re-runs
in the next Tracker flush cycle).
2. The subscription detects this, and given that anything is possible in next computation

62
Publications and Data Loading

run, marks itself for destruction.


3. The computation re-runs, with .subscribe() being re-called either with the same or
different arguments.
4. If the subscription is run with the same arguments then the "new" subscription discovers
the old "marked for destruction" subscription that's sitting around, with the same data
already ready, and simply reuses that.
5. If the subscription is run with different arguments, then a new subscription is created,
which connects to the publication on the server.
6. At the end of the flush cycle (i.e. after the computation is done re-running), the old
subscription checks to see if it was re-used, and if not, sends a message to the server
to tell the server to shut it down.

Step 4 above is an important detail---that the system cleverly knows not to re-subscribe if the
autorun re-runs and subscribes with the exact same arguments. This holds true even if the
new subscription is set up somewhere else in the template hierarchy. For example, if a user
navigates between two pages that both subscribe to the exact same subscription, the same
mechanism will kick in and no unnecessary subscribing will happen.

Publication behavior when arguments change


It's also worth knowing a little about what happens on the server when the new subscription
is started and the old one is stopped.

The server explicitly waits until all the data is sent down (the new subscription is ready) for
the new subscription before removing the data from the old subscription. The idea here is to
avoid flicker---you can, if desired, continue to show the old subscription's data until the new
data is ready, then instantly switch over to the new subscription's complete data set.

What this means is in general, when changing subscriptions, there'll be a period where you
are over-subscribed and there is more data on the client than you strictly asked for. This is
one very important reason why you should always fetch the same data that you have
subscribed to (don't "over-fetch").

Paginating subscriptions
A very common pattern of data access is pagination. This refers to the practice of fetching
an ordered list of data one "page" at a time---typically some number of items, say twenty.

There are two styles of pagination that are commonly used, a "page-by-page" style---where
you show only one page of results at a time, starting at some offset (which the user can
control), and "infinite-scroll" style, where you show an increasing number of pages of items,
as the user moves through the list (this is the typical "feed" style user interface).

63
Publications and Data Loading

In this section, we'll consider a publication/subscription technique for the second, infinite-
scroll style pagination. The page-by-page technique is a little tricker to handle in Meteor, due
to it being difficult to calculate the offset on the client. If you need to do so, you can follow
many of the same techniques that we use here and use the percolate:find-from-
publication package to keep track of which records have come from your publication.

In an infinite scroll publication, we simply need to add a new argument to our publication
controlling how many items to load. Suppose we wanted to paginate the todo items in our
Todos example app:

const MAX_TODOS = 1000;

Meteor.publish('todos.inList', function(listId, limit) {


new SimpleSchema({
listId: { type: String },
limit: { type: Number }
}).validate({ listId, limit });

const options = {
sort: {createdAt: -1},
limit: Math.min(limit, MAX_TODOS)
};

// ...
});

It's important that we set a sort parameter on our query (to ensure a repeatable order of
list items as more pages are requested), and that we set an absolute maximum on the
number of items a user can request (at least in the case where lists can grow without
bound).

Then on the client side, we'd set some kind of reactive state variable to control how many
items to request:

Template.Lists_show_page.onCreated(function() {
this.getListId = () => FlowRouter.getParam('_id');

this.autorun(() => {
this.subscribe('todos.inList',
this.getListId(), this.state.get('requestedTodos'));
});
});

We'd increment that requestedTodos variable when the user clicks "load more" (or perhaps
just when they scroll to the bottom of the page).

64
Publications and Data Loading

One piece of information that's very useful to know when paginating data is the total number
of items that you could see. The tmeasday:publish-counts package can be useful to publish
this. We could add a Lists.todoCount publication like so

Meteor.publish('Lists.todoCount', function({ listId }) {


new SimpleSchema({
listId: {type: String}
}).validate({ listId });

Counts.publish(this, `Lists.todoCount.${listId}`, Todos.find({listId}));


});

Then on the client, after subscribing to that publication, we can access the count with

Counts.get(`Lists.todoCount.${listId}`)

Client-side data with reactive stores


In Meteor, persistent or shared data comes over the wire on publications. However, there are
some types of data which doesn't need to be persistent or shared between users. For
instance, the "logged-in-ness" of the current user, or the route they are currently viewing.

Although client-side state is often best contained as state of an individual template (and
passed down the template hierarchy as arguments where necessary), sometimes you have
a need for "global" state that is shared between unrelated sections of the template hierarchy.

Usually such state is stored in a global singleton object which we can call a store. A
singleton is a data structure of which only a single copy logically exists. The current user and
the router from above are typical examples of such global singletons.

Types of stores
In Meteor, it's best to make stores reactive data sources, as that way they tie most naturally
into the rest of the ecosystem. There are a few different packages you can use for stores.

If the store is single-dimensional, you can probably use a ReactiveVar to store it (provided
by the reactive-var package). A ReactiveVar has two properties, get() and set() :

DocumentHidden = new ReactiveVar(document.hidden);


$(window).on('visibilitychange', (event) => {
DocumentHidden.set(document.hidden);
});

65
Publications and Data Loading

If the store is multi-dimensional, you may want to use a ReactiveDict (from the reactive-
dict package):

const $window = $(window);


function getDimensions() {
return {
width: $window.width(),
height: $window.height()
};
};

WindowSize = new ReactiveDict();


WindowSize.set(getDimensions());
$window.on('resize', () => {
WindowSize.set(getDimensions());
});

The advantage of a ReactiveDict is you can access each property individually


( WindowSize.get('width') ), and the dict will diff the field and track changes on it individually
(so your template will re-render less often for instance).

If you need to query the store, or store many related items, it's probably a good idea to use a
Local Collection (see the Collections Article).

Accessing stores
You should access stores in the same way you'd access other reactive data in your
templates---that means centralizing your store access, much like you centralize your
subscribing and data fetch. For a Blaze template, that's either in a helper, or from within a
this.autorun() inside an onCreated() callback.

This way you get the full reactive power of the store.

Updating stores
If you need to update a store as a result of user action, you'd update the store from an event
handler, just like you call Methods.

If you need to perform complex logic in the update (e.g. not just call .set() etc), it's a good
idea to define a mutator on the store. As the store is a singleton, you can just attach a
function to the object directly:

66
Publications and Data Loading

WindowSize.simulateMobile = (device) => {


if (device === 'iphone6s') {
this.set({width: 750, height: 1334});
}
}

Advanced publications
Sometimes, the simple mechanism of returning a query from a publication function won't
cover your needs. In those situations, there are some more powerful publication patterns
that you can use.

Publishing relational data


It's common to need related sets of data from multiple collections on a given page. For
instance, in the Todos app, when we render a todo list, we want the list itself, as well as the
set of todos that belong to that list.

One way you might do this is to return more than one cursor from your publication function:

Meteor.publish('todos.inList', function(listId) {
new SimpleSchema({
listId: {type: String}
}).validate({ listId });

const list = Lists.findOne(listId);

if (list && (!list.userId || list.userId === this.userId)) {


return [
Lists.find(listId),
Todos.find({listId})
];
} else {
// The list doesn't exist, or the user isn't allowed to see it.
// In either case, make it appear like there is no list.
return this.ready();
}
});

However, this example will not work as you might expect. The reason is that reactivity
doesn't work in the same way on the server as it does on the client. On the client, if anything
in a reactive function changes, the whole function will re-run, and the results are fairly
intuitive.

67
Publications and Data Loading

On the server however, the reactivity is limited to the behavior of the cursors you return from
your publish functions. You'll see any changes to the data that matches their queries, but
their queries will never change.

So in the case above, if a user subscribes to a list that is later made private by another user,
although the list.userId will change to a value that no longer passes the condition, the
body of the publication will not re-run, and so the query to the Todos collection ( {listId} )
will not change. So the first user will continue to see items they shouldn't.

However, we can write publications that are properly reactive to changes across collections.
To do this, we use the reywood:publish-composite package.

The way this package works is to first establish a cursor on one collection, and then explicitly
set up a second level of cursors on a second collection with the results of the first cursor.
The package uses a query observer behind the scenes to trigger the subscription to change
and queries to re-run whenever the source data changes.

Meteor.publishComposite('todos.inList', function(listId) {
new SimpleSchema({
listId: {type: String}
}).validate({ listId });

const userId = this.userId;

return {
find() {
const query = {
_id: listId,
$or: [{userId: {$exists: false}}, {userId}]
};

// We only need the _id field in this query, since it's only
// used to drive the child queries to get the todos
const options = {
fields: { _id: 1 }
};

return Lists.find(query, options);


},

children: [{
find(list) {
return Todos.find({ listId: list._id }, { fields: Todos.publicFields });
}
}]
};
});

68
Publications and Data Loading

In this example, we write a complicated query to make sure that we only ever find a list if we
are allowed to see it, then, once per list we find (which can be one or zero times depending
on access), we publish the todos for that list. Publish Composite takes care of stopping and
starting the dependent cursors if the list stops matching the original query or otherwise.

Complex authorization
We can also use publish-composite to perform complex authorization in publications. For
instance, consider if we had a Todos.admin.inList publication that allowed an admin to
bypass default publication's security for users with an admin flag set.

We might want to write:

Meteor.publish('Todos.admin.inList', function({ listId }) {


new SimpleSchema({
listId: {type: String}
}).validate({ listId });

const user = Meteor.users.findOne(this.userId);

if (user && user.admin) {


// We don't need to worry about the list.userId changing this time
return [
Lists.find(listId),
Todos.find({listId})
];
} else {
return this.ready();
}
});

However, due to the same reasons discussed above, the publication will not re-run if the
user's admin status changes. If this is something that is likely to happen and reactive
changes are needed, then we'll need to make the publication reactive. We can do this via
the same technique as above however:

69
Publications and Data Loading

Meteor.publishComposite('Todos.admin.inList', function(listId) {
new SimpleSchema({
listId: {type: String}
}).validate({ listId });

const userId = this.userId;


return {
find() {
return Meteor.users.find({userId, admin: true});
},
children: [{
find() {
// We don't need to worry about the list.userId changing this time
return [
Lists.find(listId),
Todos.find({listId})
];
}
}]
};
});

Custom publications with the low level API


In all of our examples so far (outside of using Meteor.publishComposite() ) we've returned a
cursor from our Meteor.publish() handlers. Doing this ensures Meteor takes care of the job
of keeping the contents of that cursor in sync between the server and the client. However,
there's another API you can use for publish functions which is closer to the way the
underlying Distributed Data Protocol (DDP) works.

DDP uses three main messages to communicate changes in the data for a publication: the
added , changed and removed messages. So, we can similarly do the same for a

publication:

70
Publications and Data Loading

Meteor.publish('custom-publication', function() {
// We can add documents one at a time
this.added('collection-name', 'id', {field: 'values'});

// We can call ready to indicate to the client that the initial document sent has be
en sent
this.ready();

// We may respond to some 3rd party event and want to send notifications
Meteor.setTimeout(() => {
// If we want to modify a document that we've already added
this.changed('collection-name', 'id', {field: 'new-value'});

// Or if we don't want the client to see it any more


this.removed('collection-name', 'id');
});

// It's very important to clean up things in the subscription's onStop handler


this.onStop(() => {
// Perhaps kill the connection with the 3rd party server
});
});

From the client's perspective, data published like this doesn't look any different---there's
actually no way for the client to know the difference as the DDP messages are the same. So
even if you are connecting to, and mirroring, some esoteric data source, on the client it'll
appear like any other Mongo collection.

One point to be aware of is that if you allow the user to modify data in the "pseudo-
collection" you are publishing in this fashion, you'll want to be sure to re-publish the
modifications to them via the publication, to achieve an optimistic user experience.

Subscription lifecycle
Although you can use publications and subscriptions in Meteor via an intuitive
understanding, sometimes it's useful to know exactly what happens under the hood when
you subscribe to data.

Suppose you have a simple publication of the following form:

Meteor.publish('Posts.all', function() {
return Posts.find({}, {limit: 10});
});

Then when a client calls Meteor.subscribe('Posts.all') the following things happen inside
Meteor:

71
Publications and Data Loading

1. The client sends a sub message with the name of the subscription over DDP.

2. The server starts up the subscription by running the publication handler function.

3. The publication handler identifies that the return value is a cursor. This enables a
convenient mode for publishing cursors.

4. The server sets up a query observer on that cursor, unless such an observer already
exists on the server (for any user), in which case that observer is re-used.

5. The observer fetches the current set of documents matching the cursor, and passes
them back to the subscription (via the this.added() callback).

6. The subscription passes the added documents to the subscribing client's connection
mergebox, which is an on-server cache of the documents that have been published to
this particular client. Each document is merged with any existing version of the
document that the client knows about, and an added (if the document is new to the
client) or changed (if it is known but this subscription is adding or changing fields) DDP
message is sent.

Note that the mergebox operates at the level of top-level fields, so if two subscriptions
publish nested fields (e.g. sub1 publishes doc.a.b = 7 and sub2 publishes doc.a.c =
8 ), then the "merged" document might not look as you expect (in this case doc.a = {c:

8} , if sub2 happens second).

7. The publication calls the .ready() callback, which sends the DDP ready message to
the client. The subscription handle on the client is marked as ready.

8. The observer observes the query. Typically, it uses MongoDB's Oplog to notice changes
that affect the query. If it sees a relevant change, like a new matching document or a
change in a field on a matching document, it calls into the subscription (via .added() ,
.changed() or .removed() ), which again sends the changes to the mergebox, and

then to the client via DDP.

This continues until the client stops the subscription, triggering the following behavior:

1. The client sends the unsub DDP message.

2. The server stops its internal subscription object, triggering the following effects:

3. Any this.onStop() callbacks setup by the publish handler run. In this case, it is a
single automatic callback setup when returning a cursor from the handler, which stops
the query observer and cleans it up if necessary.

4. All documents tracked by this subscription are removed from the mergebox, which may
or may not mean they are also removed from the client.

72
Publications and Data Loading

5. The nosub message is sent to the client to indicate that the subscription has stopped.

Working with REST APIs


Publications and subscriptions are the primary way of dealing with data in Meteor's DDP
protocol, but lots of data sources use the popular REST protocol for their API. It's useful to
be able to convert between the two.

Loading data from a REST endpoint with a publication


As a concrete example of using the low-level API, consider the situation where you have
some 3rd party REST endpoint which provides a changing set of data that's valuable to your
users. How do you make that data available?

One option would be to provide a Method that simply proxies through to the endpoint, for
which it's the client's responsibility to poll and deal with the changing data as it comes in. So
then it's the clients problem to deal with keeping a local data cache of the data, updating the
UI when changes happen, etc. Although this is possible (you could use a Local Collection to
store the polled data, for instance), it's simpler, and more natural to create a publication that
does this polling for the client.

A pattern for turning a polled REST endpoint looks something like this:

73
Publications and Data Loading

const POLL_INTERVAL = 5000;

Meteor.publish('polled-publication', function() {
const publishedKeys = {};

const poll = () => {


// Let's assume the data comes back as an array of JSON documents, with an _id fie
ld, for simplicity
const data = HTTP.get(REST_URL, REST_OPTIONS);

data.forEach((doc) => {
if (publishedKeys[doc._id]) {
this.changed(COLLECTION_NAME, doc._id, doc);
} else {
publishedKeys[doc._id] = true;
this.added(COLLECTION_NAME, doc._id, doc);
}
});
};

poll();
this.ready();

const interval = Meteor.setInterval(poll, POLL_INTERVAL);

this.onStop(() => {
Meteor.clearInterval(interval);
});
});

Things can get more complicated; for instance you may want to deal with documents being
removed, or share the work of polling between multiple users (in a case where the data
being polled isn't private to that user), rather than doing the exact same poll for each
interested user.

Accessing a publication as a REST endpoint


The opposite scenario occurs when you want to publish data to be consumed by a 3rd party,
typically over REST. If the data we want to publish is the same as what we already publish
via a publication, then we can use the simple:rest package to do this really easily.

In the Todos example app, we have done this, and you can now access our publications
over HTTP:

74
Publications and Data Loading

$ curl localhost:3000/publications/lists.public
{
"Lists": [
{
"_id": "rBt5iZQnDpRxypu68",
"name": "Meteor Principles",
"incompleteCount": 7
},
{
"_id": "Qzc2FjjcfzDy3GdsG",
"name": "Languages",
"incompleteCount": 9
},
{
"_id": "TXfWkSkoMy6NByGNL",
"name": "Favorite Scientists",
"incompleteCount": 6
}
]
}

You can also access authenticated publications (such as lists.private ). Suppose we've
signed up (via the web UI) as user@example.com , with the password password , and created
a private list. Then we can access it as follows:

# First, we need to "login" on the commandline to get an access token


$ curl localhost:3000/users/login -H "Content-Type: application/json" --data '{"email
": "user@example.com", "password": "password"}'
{
"id": "wq5oLMLi2KMHy5rR6",
"token": "6PN4EIlwxuVua9PFoaImEP9qzysY64zM6AfpBJCE6bs",
"tokenExpires": "2016-02-21T02:27:19.425Z"
}

# Then, we can make an authenticated API call


$ curl localhost:3000/publications/lists.private -H "Authorization: Bearer 6PN4EIlwxuV
ua9PFoaImEP9qzysY64zM6AfpBJCE6bs"
{
"Lists": [
{
"_id": "92XAn3rWhjmPEga4P",
"name": "My Private List",
"incompleteCount": 5,
"userId": "wq5oLMLi2KMHy5rR6"
}
]
}

75
Publications and Data Loading

76
Methods

After reading this article, you'll know:

1. What Methods are in Meteor and how they work in detail.


2. Best practices for defining and calling Methods.
3. How to throw and handle errors with Methods.
4. How to call a Method from a form.

What is a Method?
Methods are Meteor's remote procedure call (RPC) system, used to save user input events
and data that come from the client. If you're familiar with REST APIs or HTTP, you can think
of them like POST requests to your server, but with many nice features optimized for
building a modern web application. Later on in this article, we'll go into detail about some of
the benefits you get from Methods that you wouldn't get from an HTTP endpoint.

At its core, a Method is an API endpoint for your server; you can define a Method on the
server and its counterpart on the client, then call it with some data, write to the database,
and get the return value in a callback. Meteor Methods are also tightly integrated with the
pub/sub and data loading systems of Meteor to allow for Optimistic UI - the ability to simulate
server-side actions on the client to make your app feel faster than it actually is.

We'll be referring to Meteor Methods with a capital M to differentiate them from class
methods in JavaScript.

Defining and calling Methods


Basic Method
In a basic app, defining a Meteor Method is as simple as defining a function. In a complex
app, you want a few extra features to make Methods more powerful and easily testable.
First, we're going to go over how to define a Method using the Meteor core API, and in a
later section we'll go over how to use a helpful wrapper package we've created to enable a
more powerful Method workflow.

Defining
Here's how you can use the built-in Meteor.methods API to define a Method. Note that
Methods should always be defined in common code loaded on the client and the server to
enable Optimistic UI. If you have some secret code in your Method, consult the Security
article for how to hide it from the client.

77
Methods

This example uses the aldeed:simple-schema package, which is recommended in several


other articles, to validate the Method arguments.

Meteor.methods({
'todos.updateText'({ todoId, newText }) {
new SimpleSchema({
todoId: { type: String },
newText: { type: String }
}).validate({ todoId, newText });

const todo = Todos.findOne(todoId);

if (!todo.editableBy(this.userId)) {
throw new Meteor.Error('todos.updateText.unauthorized',
'Cannot edit todos in a private list that is not yours');
}

Todos.update(todoId, {
$set: { text: newText }
});
}
});

Calling
This Method is callable from the client and server using Meteor.call . Note that you should
only use a Method in the case where some code needs to be callable from the client; if you
just want to modularize code that is only going to be called from the server, use a regular
JavaScript function, not a Method.

Here's how you can call this Method from the client:

Meteor.call('todos.updateText', {
todoId: '12345',
newText: 'This is a todo item.'
}, (err, res) => {
if (err) {
alert(err);
} else {
// success!
}
});

If the Method throws an error, you get that in the first argument of the callback. If the Method
succeeds, you get the result in the second argument and the first argument err will be
undefined . For more information about errors, see the section below about error handling.

78
Methods

Advanced Method boilerplate


Meteor Methods have several features which aren't immediately obvious, but every complex
app will need them at some point. These features were added incrementally over several
years in a backwards-compatible fashion, so unlocking the full capabilities of Methods
requires a good amount of boilerplate. In this article we will first show you all of the code you
need to write for each feature, then the next section will talk about a Method wrapper
package we have developed to make it easier.

Here's some of the functionality an ideal Method would have:

1. Run validation code by itself without running the Method body.


2. Easily override the Method for testing.
3. Easily call the Method with a custom user ID, especially in tests (as recommended by
the Discover Meteor two-tiered methods pattern).
4. Refer to the Method via JS module rather than a magic string.
5. Get the Method simulation return value to get IDs of inserted documents.
6. Avoid calling the server-side Method if the client-side validation failed, so we don't waste
server resources.

Defining

79
Methods

export const updateText = {


name: 'todos.updateText',

// Factor out validation so that it can be run independently (1)


validate(args) {
new SimpleSchema({
todoId: { type: String },
newText: { type: String }
}).validate(args)
},

// Factor out Method body so that it can be called independently (3)


run({ todoId, newText }) {
const todo = Todos.findOne(todoId);

if (!todo.editableBy(this.userId)) {
throw new Meteor.Error('todos.updateText.unauthorized',
'Cannot edit todos in a private list that is not yours');
}

Todos.update(todoId, {
$set: { text: newText }
});
},

// Call Method by referencing the JS object (4)


// Also, this lets us specify Meteor.apply options once in
// the Method implementation, rather than requiring the caller
// to specify it at the call site.
call(args, callback) {
const options = {
returnStubValue: true, // (5)
throwStubExceptions: true // (6)
}

Meteor.apply(this.name, [args], options, callback);


}
};

// Actually register the method with Meteor's DDP system


Meteor.methods({
[updateText.name]: function (args) {
updateText.validate.call(this, args);
updateText.run.call(this, args);
}
})

Calling
Now calling the Method is as simple as calling a JavaScript function:

80
Methods

import { updateText } from './path/to/methods.js';

// Call the Method


updateText.call({
todoId: '12345',
newText: 'This is a todo item.'
}, (err, res) => {
if (err) {
alert(err);
} else {
// success!
}
});

// Call the validation only


updateText.validate({ wrong: 'args'});

// Call the Method with custom userId in a test


updateText.run.call({ userId: 'abcd' }, {
todoId: '12345',
newText: 'This is a todo item.'
});

As you can see, this approach to calling Methods results in a better development workflow -
you can more easily deal with the different parts of the Method separately and test your code
more easily without having to deal with Meteor internals. But this approach requires you to
write a lot of boilerplate on the Method definition side.

Advanced Methods with mdg:validated-method


To alleviate some of the boilerplate that's involved in correct Method definitions, we've
published a wrapper package called mdg:validated-method that does most of this for you.
Here's the same Method as above, but defined with the package:

81
Methods

import { ValidatedMethod } from 'meteor/mdg:validated-method';

export const updateText = new ValidatedMethod({


name: 'todos.updateText',
validate: new SimpleSchema({
todoId: { type: String },
newText: { type: String }
}).validator(),
run({ todoId, newText }) {
const todo = Todos.findOne(todoId);

if (!todo.editableBy(this.userId)) {
throw new Meteor.Error('todos.updateText.unauthorized',
'Cannot edit todos in a private list that is not yours');
}

Todos.update(todoId, {
$set: { text: newText }
});
}
});

You call it the same way you call the advanced Method above, but the Method definition is
significantly simpler. We believe this style of Method lets you clearly see the important parts
- the name of the Method sent over the wire, the format of the expected arguments, and the
JavaScript namespace by which the Method can be referenced. Validated methods only
accept a single argument and a callback function.

Error handling
In regular JavaScript functions, you indicate errors by throwing an Error object. Throwing
errors from Meteor Methods works almost the same way, but a bit of complexity is
introduced by the fact that in some cases the error object will be sent over a websocket back
to the client.

Throwing errors from a Method


Meteor introduces two new types of JavaScript errors: Meteor.Error and ValidationError .
These and the regular JavaScript Error type should be used in different situations:

Regular `Error` for internal server errors

82
Methods

When you have an error that doesn't need to be reported to the client, but is internal to the
server, throw a regular JavaScript error object. This will be reported to the client as a totally
opaque internal server error with no details.

Meteor.Error for general runtime errors


When the server was not able to complete the user's desired action because of a known
condition, you should throw a descriptive Meteor.Error object to the client. In the Todos
example app, we use these to report situations where the current user is not authorized to
complete a certain action, or where the action is not allowed within the app - for example,
deleting the last public list.

Meteor.Error takes three arguments: error , reason , and details .

1. error should be a short, unique, machine-readable error code string that the client can

interpret to understand what happened. It's good to prefix this with the name of the
Method for easy internationalization, for example: 'todos.updateText.unauthorized' .
2. reason should be a short description of the error for the developer. It should give your

coworker enough information to be able to debug the error. The reason parameter
should not be printed to the end user directly, since this means you now have to do
internationalization on the server before sending the error message, and the UI
developer has to worry about the Method implementation when thinking about what will
be displayed in the UI.
3. details is optional, and can be used where extra data will help the client understand

what is wrong. In particular, it can be combined with the error field to print a more
helpful error message to the end user.

ValidationError for argument validation errors


When a Method call fails because the arguments are of the wrong type, it's good to throw a
ValidationError . This works just like Meteor.Error , but is a custom constructor that

enforces a standard error format that can be read by different form and validation libraries. In
particular, if you are calling this Method from a form, throwing a ValidationError will make it
easy to display nice error messages next to particular fields in the form.

When you use mdg:validated-method with aldeed:simple-schema as demonstrated above,


this type of error is thrown for you.

Read more about the error format in the mdg:validation-error docs.

Handling errors

83
Methods

When you call a Method, any errors thrown by it will be returned in the callback. At this point,
you should identify which error type it is and display the appropriate message to the user. In
this case, it is unlikely that the Method will throw a ValidationError or an internal server
error, so we will only handle the unauthorized error:

// Call the Method


updateText.call({
todoId: '12345',
newText: 'This is a todo item.'
}, (err, res) => {
if (err) {
if (err.error === 'todos.updateText.unauthorized') {
// Displaying an alert is probably not what you would do in
// a real app; you should have some nice UI to display this
// error, and probably use an i18n library to generate the
// message from the error code.
alert('You aren\'t allowed to edit this todo item');
} else {
// Unexpected error, handle it in the UI somehow
}
} else {
// success!
}
});

We'll talk about how to handle the ValidationError in the section on forms below.

Errors in Method simulation


When a Method is called, it usually runs twice---once on the client to simulate the result for
Optimistic UI, and again on the server to make the actual change to the database. This
means that if your Method throws an error, it will likely fail on the client and the server. For
this reason, ValidatedMethod turns on undocumented option in Meteor to avoid calling the
server-side implementation if the simulation throws an error.

While this behavior is good for saving server resources in cases where a Method will
certainly fail, it's important to make sure that the simulation doesn't throw an error in cases
where the server Method would have succeeded (for example, if you didn't load some data
on the client that the Method needs to do the simulation properly). In this case, you can wrap
server-side-only logic in a block that checks for a method simulation:

if (!this.isSimulation) {
// Logic that depends on server environment here
}

84
Methods

Calling a Method from a form


The main thing enabled by the ValidationError convention is simple integration between
Methods and the forms that call them. In general, your app is likely to have a one-to-one
mapping of forms in the UI to Methods. First, let's define a Method for our business logic:

// This Method encodes the form validation requirements.


// By defining them in the Method, we do client and server-side
// validation in one place.
export const insert = new ValidatedMethod({
name: 'Invoices.methods.insert',
validate: new SimpleSchema({
email: { type: String, regEx: SimpleSchema.RegEx.Email },
description: { type: String, min: 5 },
amount: { type: String, regEx: /^\d*\.(\d\d)?$/ }
}).validator(),
run(newInvoice) {
// In here, we can be sure that the newInvoice argument is
// validated.

if (!this.userId) {
throw new Meteor.Error('Invoices.methods.insert.not-logged-in',
'Must be logged in to create an invoice.');
}

Invoices.insert(newInvoice)
}
});

Let's define a simple HTML form:

85
Methods

<template name="Invoices_newInvoice">
<form class="Invoices_newInvoice">
<label for="email">Recipient email</label>
<input type="email" name="email" />
{{#each error in errors "email"}}
<div class="form-error">{{error}}</div>
{{/each}}

<label for="description">Item description</label>


<input type="text" name="description" />
{{#each error in errors "description"}}
<div class="form-error">{{error}}</div>
{{/each}}

<label for="amount">Amount owed</label>


<input type="text" name="amount" />
{{#each error in errors "amount"}}
<div class="form-error">{{error}}</div>
{{/each}}
</form>
</template>

Now, let's write some JavaScript to handle this form nicely:

86
Methods

import { insert } from '../api/invoices/methods.js';

Template.Invoices_newInvoice.onCreated(function() {
this.errors = new ReactiveDict();
});

Template.Invoices_newInvoice.helpers({
errors(fieldName) {
return this.errors.get(fieldName);
}
});

Template.Invoices_newInvoice.events({
'submit .Invoices_newInvoice'(event, instance) {
const data = {
email: event.target.email.value,
description: event.target.description.value,
amount: event.target.amount.value
};

insert.call(data, (err, res) => {


if (err) {
if (err.error === 'validation-error') {
// Initialize error object
const errors = {
email: [],
description: [],
amount: []
};

// Go through validation errors returned from Method


err.details.forEach((fieldError) => {
// XXX i18n
errors[fieldError.name].push(fieldError.type);
});

// Update ReactiveDict, errors will show up in the UI


instance.errors.set(errors);
}
}
});
}
});

As you can see, there is a fair amount of boilerplate to handle errors nicely in a form, but
most of it can be easily abstracted by an off-the-shelf form framework or a simple
application-specific wrapper of your own design.

Loading data with Methods

87
Methods

Since Methods can work as general purpose RPCs, they can also be used to fetch data
instead of publications. There are some advantages and some disadvantages to this
approach compared with loading data through publications, and at the end of the day we
recommend always using publications to load data.

Methods can be useful to fetch the result of a complex computation from the server that
doesn't need to update when the server data changes. The biggest disadvantage of fetching
data through Methods is that the data won't be automatically loaded into Minimongo,
Meteor's client-side data cache, so you'll need to manage the lifecycle of that data manually.

Using a local collection to store and display data fetched


from a Method
Collections are a very convenient way of storing data on the client side. If you're fetching
data using something other than subscriptions, you can put it in a collection manually. Let's
look at an example where we have a complex algorithm for calculating average scores from
a series of games for a number of players. We don't want to use a publication to load this
data because we want to control exactly when it runs, and don't want the data to be cached
automatically.

First, you need to create a local collection - this is a collection that exists only on the client
side and is not tied to a database collection on the server. Read more in the Collections
article.

// In client-side code, declare a local collection


// by passing `null` as the argument
ScoreAverages = new Mongo.Collection(null);

Now, if you fetch data using a Method, you can put into this collection:

import { calculateAverages } from '../api/games/methods.js';

function updateAverages() {
// Clean out result cache
ScoreAverages.remove({});

// Call a Method that does an expensive computation


calculateAverages.call((err, res) => {
res.forEach((item) => {
ScoreAverages.insert(item);
});
});
}

88
Methods

We can now use the data from the local collection ScoreAverages inside a UI component
exactly the same way we would use a regular MongoDB collection. Instead of it updating
automatically, we'll need to call updateAverages every time we need new results.

Advanced concepts
While you can easily use Methods in a simple app by following the Meteor introductory
tutorial, it's important to understand exactly how they work to use them effectively in a
production app. One of the downsides of using a framework like Meteor that does a lot for
you under the hood is that you don't always understand what is going on, so it's good to
learn some of the core concepts.

Method call lifecycle


Here's exactly what happens, in order, when a Method is called:

1. Method simulation runs on the client


If we defined this Method in client and server code, as all Methods should be, a Method
simulation is executed in the client that called it.

The client enters a special mode where it tracks all changes made to client-side collections,
so that they can be rolled back later. When this step is complete, the user of your app sees
their UI update instantly with the new content of the client-side database, but the server
hasn't received any data yet.

If an exception is thrown from the Method simulation, then by default Meteor ignores it and
continues to step (2). If you are using ValidatedMethod or pass a special
throwStubExceptions option to Meteor.apply , then an exception thrown from the simulation

will stop the server-side Method from running at all.

The return value of the Method simulation is discarded, unless the returnStubValue option
is passed when calling the Method, in which case it is returned to the Method caller.
ValidatedMethod passes this option by default.

2. A `method` DDP message is sent to the server


The Meteor client constructs a DDP message to send to the server. This includes the
Method name, arguments, and an automatically generated Method ID that represents this
particular Method invocation.

89
Methods

3. Method runs on the server


When the server receives the message, it executes the Method code again on the server.
The client side version was a simulation that will be rolled back later, but this time it's the real
version that is writing to the actual database. Running the actual Method logic on the server
is crucial because the server is a trusted environment where we know that security-critical
code will run the way we expect.

4. Return value is sent to the client


Once the Method has finished running on the server, it sends a result message to the
client with the Method ID generated in step 2, and the return value itself. The client stores
this for later use, but doesn't call the Method callback yet. If you pass the onResultReceived
option to Meteor.apply , that callback is fired.

5. Any DDP publications affected by the Method are


updated
If we have any publications on the page that have been affected by the database writes from
this Method, the server sends the appropriate updates to the client. Note that the client data
system doesn't reveal these updates to the app UI until the next step.

6. `updated` message sent to the client, data replaced with


server result, Method callback fires
After the relevant data updates have been sent to the correct client, the server sends back
the last message in the Method life cycle - the DDP updated message with the relevant
Method ID. The client rolls back any changes to client side data made in the Method
simulation in step 1, and replaces them with the actual changes sent from the server in step
5.

Lastly, the callback passed to Meteor.call actually fires with the return value from step 4.
It's important that the callback waits until the client is up to date, so that your Method
callback can assume that the client state reflects any changes done inside the Method.

Error case
In the list above, we didn't cover the case when the Method execution on the server throws
an error. In that case, there is no return value, and the client gets an error instead. The
Method callback is fired instantly with the returned error as the first argument. Read more
about error handling in the section about errors below.

90
Methods

Benefits of Methods over REST


We believe Methods provide a much better primitive for building modern applications than
REST endpoints built on HTTP. Let's go over some of the things you get for free with
Methods that you would have to worry about if using HTTP. The purpose of this section is
not to convince you that REST is bad - it's just to remind you that you don't need to handle
these things yourself in a Meteor app.

Methods use synchronous-style APIs, but are non-blocking


You may notice in the example Method above, we didn't need to write any callbacks when
interacting with MongoDB, but the Method still has the non-blocking properties that people
associate with Node.js and callback-style code. Meteor uses a coroutine library called Fibers
to enable you to write code that uses return values and throws errors, and avoid dealing with
lots of nested callbacks.

Methods always run and return in order


When accessing a REST API, you will sometimes run into a situation where you make two
requests one after the other, but the results arrive out of order. Meteor's underlying
machinery makes sure this never happens with Methods. When multiple Method calls are
received from the same client, Meteor runs each Method to completion before starting the
next one. If you need to disable this functionality for one particularly long-running Method,
you can use this.unblock() to allow the next Method to run while the current one is still in
progress. Also, since Meteor is based on Websockets instead of HTTP, all Method calls and
results are guaranteed to arrive in the order they are sent. You can also pass a special
option wait: true to Meteor.apply to wait to send a particular Method until all others have
returned, and not send any other Methods until this one returns.

Change tracking for Optimistic UI


When Method simulations and server-side executions run, Meteor tracks any resulting
changes to the database. This is what lets the Meteor data system roll back the changes
from the Method simulation and replace them with the actual writes from the server. Without
this automatic database tracking, it would be very difficult to implement a correct Optimistic
UI system.

Calling a Method from another Method

91
Methods

Sometimes, you'll want to call a Method from another Method. Perhaps you already have
some functionality implemented and you want to add a wrapper that fills in some of the
arguments automatically. This is a totally fine pattern, and Meteor does some nice things for
you:

1. Inside a client-side Method simulation, calling another Method doesn't fire off an extra
request to the server - the assumption is that the server-side implementation of the
Method will do it. However, it does run the simulation of the called Method, so that the
simulation on the client closely matches what will happen on the server.
2. Inside a Method execution on the server, calling another Method runs that Method as if
it were called by the same client. That means the Method runs as usual, and the context
- userId , connection , etc - are taken from the original Method call.

Consistent ID generation and optimistic UI


When you insert documents into Minimongo from the client-side simulation of a Method, the
_id field of each document is a random string. When the Method call is executed on the

server, the IDs are generated again before being inserted into the database. If it were
implemented naively, it could mean that the IDs generated on the server are different, which
would cause undesirable flickering and re-renders in the UI when the Method simulation was
rolled back and replaced with the server data. But this is not the case in Meteor!

Each Meteor Method invocation shares a random generator seed with the client that called
the Method, so any IDs generated by the client and server Methods are guaranteed to be
the same. This means you can safely use the IDs generated on the client to do things while
the Method is being sent to the server, and be confident that the IDs will be the same when
the Method finishes. One case where this is particularly useful is if you want to create a new
document in the database, then immediately redirect to a URL that contains that new
document's ID.

Method retries
If you call a Method from the client, and the user's Internet connection disconnects before
the result is received, Meteor assumes that the Method didn't actually run. When the
connection is re-established, the Method call will be sent again. This means that, in certain
situations, Methods can be sent more than once. This should only happen very rarely, but in
the case where an extra Method call could have negative consequences it is worth putting in
extra effort to ensure that Methods are idempotent - that is, calling them multiple times
doesn't result in additional changes to the database.

92
Methods

Many Method operations are idempotent by default. Inserts will throw an error if they happen
twice because the generated ID will conflict. Removes on collections won't do anything the
second time, and most update operators like $set will have the same result if run again.
The only places you need to worry about code running twice are MongoDB update operators
that stack, like $inc and $push , and calls to external APIs.

Historical comparison with allow/deny


The Meteor core API includes an alternative to Methods for manipulating data from the
client. Instead of explicitly defining Methods with specific arguments, you can instead call
insert , update , and remove directly from the client and specify security rules with allow

and deny . In the Meteor Guide, we are taking a strong position that this feature should be
avoided and Methods used instead. Read more about the problems with allow/deny in the
Security article.

Historically, there have been some misconceptions about the features of Meteor Methods as
compared with the allow/deny feature, including that it was more difficult to achieve
Optimistic UI when using Methods. However, the client-side insert , update , and remove
feature is actually implemented on top of Methods, so Methods are strictly more powerful.
You get great default Optimistic UI just by defining your Method code on the client and the
server, as described in the Method lifecycle section above.

93
Users and Accounts

After reading this article, you'll know:

1. What features in core Meteor enable user accounts


2. How to use accounts-ui for a quick prototype
3. How to use the useraccounts family of packages to build your login UI
4. How to build a fully-featured password login experience
5. How to enable login through OAuth providers like Facebook
6. How to add custom data to Meteor's users collection
7. How to manage user roles and permissions

Features in core Meteor


Before we get into all of the different user-facing accounts functionality you can add with
Meteor, let's go over some of the features built into the Meteor DDP protocol and accounts-
base package. These are the parts of Meteor that you'll definitely need to be aware of if you

have any user accounts in your app; most of everything else is optional and added/removed
via packages.

userId in DDP
DDP is Meteor's built-in pub/sub and RPC protocol. You can read about how to use it in the
Data Loading and Methods articles. In addition to the concepts of data loading and method
calls, DDP has one more feature built in - the idea of a userId field on a connection. This is
the place where login state is tracked, regardless of which accounts UI package or login
service you are using.

This built-in feature means that you always get this.userId inside Methods and
Publications, and can access the user ID on the client. This is a great starting point for
building your own custom accounts system, but most developers won't need to worry about
the mechanics, since you'll mostly be interacting with the accounts-base package instead.

`accounts-base`
This package is the core of Meteor's developer-facing user accounts functionality. This
includes:

1. A users collection with a standard schema, accessed through Meteor.users , and the
client-side singletons Meteor.userId() and Meteor.user() , which represent the login
state on the client.
2. A variety of helpful other generic methods to keep track of login state, log out, validate
users, etc. Visit the Accounts section of the docs to find a complete list.

94
Users and Accounts

3. An API for registering new login handlers, which is used by all of the other accounts
packages to integrate with the accounts system. There isn't any official documentation
for this API, but you can read more about it on the MeteorHacks blog.

Usually, you don't need to include accounts-base yourself since it's added for you if you use
accounts-password or similar, but it's good to be aware of what is what.

Fast prototyping with `accounts-ui`


Often, a complicated accounts system is not the first thing you want to build when you're
starting out with a new app, so it's useful to have something you can just drop in quickly.
This is where accounts-ui comes in - it's just one line that you drop into your app to get an
accounts system. To add it:

meteor add accounts-ui

Then just include it anywhere in a Blaze template:

{{> loginButtons}}

Then, make sure to pick a login provider; they will automatically integrate with accounts-ui :

# pick one or more of the below


meteor add accounts-password
meteor add accounts-facebook
meteor add accounts-google
meteor add accounts-github
meteor add accounts-twitter
meteor add accounts-meetup
meteor add accounts-meteor-developer

Now just open your app, follow the configuration steps, and you're good to go - if you've
done the Meteor tutorial, you've already seen this in action. Of course, in a production
application, you probably want a more custom user interface and some logic to have a more
tailored UX, but that's why we have the rest of this guide.

Here are a couple of screenshots of accounts-ui so you know what to expect:

95
Users and Accounts

Customizable UI: useraccounts


Once you've gotten your initial prototype up and running with accounts-ui , you'll want to
move to something more powerful and configurable so that you can better integrate your
login flow with the rest of your app. The useraccounts family of packages is the most
powerful set of accounts management UI controls available for Meteor today. If you need
even more customization, you can also roll your own system, but it's worth trying
useraccounts first.

Use any router or UI framework


The first thing to understand about useraccounts is that the core accounts management
logic is independent of the HTML templates and routing packages. This means you can use
useraccounts:core to build your own set of login templates. Generally, you'll want to pick

one login template package and one login routing package. The options for templates
include:

useraccounts:unstyled which lets you bring your own CSS; this one is used in the

Todos example app to make the login UI blend seamlessly with the rest of the app.

96
Users and Accounts

Pre-built templates for Bootstrap, Semantic UI, Materialize, and more. These templates
don't come with the actual CSS framework, so you can pick your favorite Bootstrap
package, for example.

While it's optional and the basic functionality will work without it, it's also a good idea to pick
a router integration:

Flow Router, the router recommended in this guide.


Iron Router, another popular router in the Meteor community.

In the example app we are using the Flow Router integration with great success. Some of
the later sections will cover how to customize the routes and templates to fit your app better.

Drop-in UI without routing


If you don't want to configure routing for your login flow, you can just drop in a self-managing
accounts screen. Wherever you want the accounts UI template to render, just include the
atForm template, like so:

{{> atForm}}

Once you configure routing according to the section below, you'll want to remove this
inclusion.

Customizing templates
For some apps, the off-the-shelf login templates provided by the various useraccounts UI
packages will work as-is, but most apps will want to customize some of the presentation.
There's a simple way to do that using the template replacement functionality of the
aldeed:template-extension package.

First, figure out which template you want to replace by looking at the source code of the
package. For example, in the useraccounts:unstyled package, the templates are listed in
this directory on GitHub. By squinting at the file names and looking for some of the HTML
strings, we can figure out that we might be interested in replacing the atPwdFormBtn
template. Let's take a look at the original template:

<template name="atPwdFormBtn">
<button type="submit" class="at-btn submit {{submitDisabled}}" id="at-btn">
{{buttonText}}
</button>
</template>

97
Users and Accounts

Once you've identified which template you need to replace, define a new template. In this
case, we want to modify the class on the button to work with the CSS for the rest of the app.
There are a few things to keep in mind when overriding a template:

1. Render the helpers in the same way the previous template did. In this case we are
using buttonText .
2. Keep any id attributes, like at-btn , since those are used for event handling.

Here's what our new override template looks like:

<template name="override-atPwdFormBtn">
<button type="submit" class="btn-primary" id="at-btn">
{{buttonText}}
</button>
</template>

Then, use the replaces function on the template to override the existing template from
useraccounts :

Template['override-atPwdFormBtn'].replaces('atPwdFormBtn');

Customizing routes
In addition to having control over the templates, you'll want to be able to control the routing
and URLs for the different views offered by useraccounts . Since Flow Router is the officially
recommended routing option for Meteor, we'll go over that in particular.

First, we need to configure the layout we want to use when rendering the accounts
templates:

AccountsTemplates.configure({
defaultTemplate: 'Auth_page',
defaultLayout: 'App_body',
defaultContentRegion: 'main',
defaultLayoutRegions: {}
});

In this case, we want to use the App_body layout template for all of the accounts-related
pages. This template has a content region called main . Now, let's configure some routes:

98
Users and Accounts

// Define these routes in a file loaded on both client and server


AccountsTemplates.configureRoute('signIn', {
name: 'signin',
path: '/signin'
});

AccountsTemplates.configureRoute('signUp', {
name: 'join',
path: '/join'
});

AccountsTemplates.configureRoute('forgotPwd');

AccountsTemplates.configureRoute('resetPwd', {
name: 'resetPwd',
path: '/reset-password'
});

Note that we have specified a password reset route. Normally, we would have to configure
Meteor's accounts system to send this route in password reset emails, but the
useraccounts:flow-routing package does it for us. Read more about configuring email flows

below.

Now that the routes are setup on the server, they can be accessed from the browser (e.g.
example.com/reset-password ). To create links to these routes in a template, it's best to use a

helper method provided by the router. For Flow Router, the arillo:flow-router-helpers
package provides a pathFor helper for just this purpose. Once installed, the following is
possible in a template:

<div class="btns-group">
<a href="{{pathFor 'signin'}}" class="btn-secondary">Sign In</a>
<a href="{{pathFor 'join'}}" class="btn-secondary">Join</a>
</div>

You can find a complete list of different available routes in the documentation the
useraccounts:flow-routing .

Further customization
useraccounts offers many other customization options beyond templates and routing. Read

the useraccounts guide to learn about all of the other options.

Password login

99
Users and Accounts

Meteor comes with a secure and fully-featured password login system out of the box. To use
it, add the package:

meteor add accounts-password

To see what options are available to you, read the complete description of the accounts-
password API in the Meteor docs.

Requiring username or email


Note: You don't have to do this if you're using useraccounts . It disables the regular
Meteor client-side account creation functions for you and does custom validation.

By default, the Accounts.createUser function provided by accounts-password allows you to


create an account with a username, email, or both. Most apps expect a specific combination
of the two, so you will certainly want to validate the new user creation:

// Ensuring every user has an email address, should be in server-side code


Accounts.validateNewUser((user) => {
new SimpleSchema({
_id: { type: String },
emails: { type: Array },
'emails.$': { type: Object },
'emails.$.address': { type: String },
'emails.$.verified': { type: Boolean },
createdAt: { type: Date },
services: { type: Object, blackbox: true }
}).validate(user);

// Return true to allow user creation to proceed


return true;
});

Multiple emails
Often, users might want to associate multiple email addresses with the same account.
accounts-password addresses this case by storing the email addresses as an array in the

user collection. There are some handy API methods to deal with adding, removing, and
verifying emails.

One useful thing to add for your app can be the concept of a "primary" email address. This
way, if the user has added multiple emails, you know where to send confirmation emails and
similar.

100
Users and Accounts

Case sensitivity
Before Meteor 1.2, all email addresses and usernames in the database were considered to
be case-sensitive. This meant that if you registered an account as AdaLovelace@example.com ,
and then tried to log in with adalovelace@example.com , you'd see an error indicating that no
user with that email exists. Of course, this can be quite confusing, so we decided to improve
things in Meteor 1.2. But the situation was not as simple as it seemed; since MongoDB
doesn't have a concept of case-insensitive indexes, it was impossible to guarantee unique
emails at the database level. For this reason, we have some special APIs for querying and
updating users which manage the case-sensitivity problem at the application level.

What does this mean for my app?


Just follow one simple rule: don't query the database by username or email directly.
Instead, use the Accounts.findUserByUsername and Accounts.findUserByEmail methods
provided by Meteor. This will run a query for you that is case-insensitive, so you will always
find the user you are looking for.

Email flows
When you have a login system for your app based on user emails, that opens up the
possibility for email-based account flows. The common thing between all of these workflows
is that they involve sending a unique link to the user's email address, which does something
special when it is clicked. Let's look at some common examples that Meteor's accounts-
password package supports out of the box:

1. Password reset. When the user clicks the link in their email, they are taken to a page
where they can enter a new password for their account.
2. User enrollment. A new user is created by an administrator, but no password is set.
When the user clicks the link in their email, they are taken to a page where they can set
a new password for their account. Very similar to password reset.
3. Email verification. When the user clicks the link in their email, the application records
that this email does indeed belong to the correct user.

Here, we'll talk about how to manage the whole process manually from start to finish.

Email works out of the box with accounts UI packages


If you want something that just works out of the box, you can use accounts-ui or
useraccounts which basically do everything for you. Only follow the directions below if you

definitely want to build all parts of the email flow yourself.

101
Users and Accounts

Sending the email


accounts-password comes with handy functions that you can call from the server to send an

email. They are named for exactly what they do:

1. Accounts.sendResetPasswordEmail

2. Accounts.sendEnrollmentEmail

3. Accounts.sendVerificationEmail

The email is generated using the email templates from Accounts.emailTemplates, and
include links generated with Accounts.urls . We'll go into more detail about customizing the
email content and URL later.

Identifying when the link is clicked


When the user receives the email and clicks the link inside, their web browser will take them
to your app. Now, you need to be able to identify these special links and act appropriately. If
you haven't customized the link URL, then you can use some built-in callbacks to identify
when the app is in the middle of an email flow.

Normally, when the Meteor client connects to the server, the first thing it does is pass the
login resume token to re-establish a previous login. However, when these callbacks from the
email flow are triggered, the resume token is not sent until your code signals that it has
finished handling the request by calling the done function that is passed into the registered
callback. This means that if you were previously logged in as user A, and then you clicked
the reset password link for user B, but then you cancelled the password reset flow by calling
done() , the client would log in as A again.

1. Accounts.onResetPasswordLink

2. Accounts.onEnrollmentLink

3. Accounts.onEmailVerificationLink

Here's how you would use one of these functions:

102
Users and Accounts

Accounts.onResetPasswordLink((token, done) => {


// Display the password reset UI, get the new password...

Accounts.resetPassword(token, newPassword, (err) => {


if (err) {
// Display error
} else {
// Resume normal operation
done();
}
});
})

If you want a different URL for your reset password page, you need to customize it using the
Accounts.urls option:

Accounts.urls.resetPassword = (token) => {


return Meteor.absoluteUrl(`reset-password/${token}`);
};

If you have customized the URL, you will need to add a new route to your router that
handles the URL you have specified, and the default Accounts.onResetPasswordLink and
friends won't work for you.

Displaying an appropriate UI and completing the process


Now that you know that the user is attempting to reset their password, set an initial
password, or verify their email, you should display an appropriate UI to allow them to do so.
For example, you might want to show a page with a form for the user to enter their new
password.

When the user submits the form, you need to call the appropriate function to commit their
change to the database. Each of these functions takes the new value and the token you got
from the event in the previous step.

1. Accounts.resetPassword - this one should be used both for resetting the password, and

enrolling a new user; it accepts both kinds of tokens.


2. Accounts.verifyEmail

After you have called one of the two functions above or the user has cancelled the process,
call the done function you got in the link callback. This will tell Meteor to get out of the
special state it enters when you're doing one of the email account flows.

Customizing accounts emails

103
Users and Accounts

You will probably want to customize the emails accounts-password will send on your behalf.
This can be easily done through the Accounts.emailTemplates API. Below is some example
code from the Todos app:

Accounts.emailTemplates.siteName = "Meteor Guide Todos Example";


Accounts.emailTemplates.from = "Meteor Todos Accounts <accounts@example.com>";

Accounts.emailTemplates.resetPassword = {
subject(user) {
return "Reset your password on Meteor Todos";
},
text(user, url) {
return `Hello!
Click the link below to reset your password on Meteor Todos.
${url}
If you didn't request this email, please ignore it.
Thanks,
The Meteor Todos team
`
},
html(user, url) {
// This is where HTML email content would go.
// See the section about html emails below.
}
};

As you can see, we can use the ES2015 template string functionality to generate a multi-line
string that includes the password reset URL. We can also set a custom from address and
email subject.

HTML emails
If you've ever needed to deal with sending pretty HTML emails from an app, you know that it
can quickly become a nightmare. Compatibility of popular email clients with basic HTML
features like CSS is notoriously spotty, so it is hard to author something that works at all.
Start with a responsive email template or framework, and then use a tool to convert your
email content into something that is compatible with all email clients. This blog post by
Mailgun covers some of the main issues with HTML email. In theory, a community package
could extend Meteor's build system to do the email compilation for you, but at the time of
writing we were not aware of any such packages.

OAuth login

104
Users and Accounts

In the distant past, it could have been a huge headache to get Facebook or Google login to
work with your app. Thankfully, most popular login providers have standardized around
some version of OAuth, and Meteor supports some of the most popular login services out of
the box.

Facebook, Google, and more


Here's a complete list of login providers for which Meteor actively maintains core packages:

1. Facebook with accounts-facebook


2. Google with accounts-google
3. GitHub with accounts-github
4. Twitter with accounts-twitter
5. Meetup with accounts-meetup
6. Meteor Developer Accounts with accounts-meteor-developer

There is a package for logging in with Weibo, but it is no longer being actively maintained.

Logging in
If you are using an off-the-shelf login UI like accounts-ui or useraccounts , you don't need
to write any code after adding the relevant package from the list above. If you are building a
login experience from scratch, you can log in programmatically using the
Meteor.loginWith<Service> function. It looks like this:

Meteor.loginWithFacebook({
requestPermissions: ['user_friends', 'public_profile', 'email']
}, (err) => {
if (err) {
// handle error
} else {
// successful login!
}
});

Configuring OAuth
There are a few points to know about configuring OAuth login:

1. Client ID and secret. It's best to keep your OAuth secret keys outside of your source
code, and pass them in through Meteor.settings. Read how in the Security article.
2. Redirect URL. On the OAuth provider's side, you'll need to specify a redirect URL. The
URL will look like: https://www.example.com/_oauth/facebook . Replace facebook with

105
Users and Accounts

the name of the service you are using. Note that you will need to configure two URLs -
one for your production app, and one for your development environment, where the
URL might be something like http://localhost:3000/_oauth/facebook .
3. Permissions. Each login service provider should have documentation about which
permissions are available. For example, here is the page for Facebook. If you want
additional permissions to the user's data when they log in, pass some of these strings in
the requestPermissions option to Meteor.loginWithFacebook or Accounts.ui.config . In
the next section we'll talk about how to retrieve that data.

Calling service API for more data


If your app supports or even requires login with an external service such as Facebook, it's
natural to also want to use that service's API to request additional data about that user. For
example, you might want to get a list of a Facebook user's photos.

First, you'll need to request the relevant permissions when logging in the user. See the
section above for how to pass those options.

Then, you need to get the user's access token. You can find this token in the Meteor.users
collection under the services field. For example, if you wanted to get a particular user's
Facebook access token:

// Given a userId, get the user's Facebook access token


const user = Meteor.users.findOne(userId);
const fbAccessToken = user.services.facebook.accessToken;

For more details about the data stored in the user database, read the section below about
accessing user data.

Now that you have the access token, you need to actually make a request to the appropriate
API. Here you have two options:

1. Use the http package to access the service's API directly. You'll probably need to
pass the access token from above in a header. For details you'll need to search the API
documentation for the service.
2. Use a package from Atmosphere or npm that wraps the API into a nice JavaScript
interface. For example, if you're trying to load data from Facebook you could use the
fbgraph npm package. Read more about how to use npm with your app in the Build
System article.

Loading and displaying user data

106
Users and Accounts

Meteor's accounts system, as implemented in accounts-base , also includes a database


collection and generic functions for getting data about users.

Currently logged in user


Once a user is logged into your app with one of the methods described above, it is useful to
be able to identify which user is logged in, and get the data provided during the registration
process.

On the client: Meteor.userId()


For code that runs on the client, the global Meteor.userId() reactive function will give you
the ID of the currently logged in user.

In addition to that core API, there are some helpful shorthand helpers: Meteor.user() ,
which is exactly equal to calling Meteor.users.findOne(Meteor.userId()) , and the {% raw %}
{{currentUser}}{% endraw %} Blaze helper that returns the value of Meteor.user() .

Note that there is a benefit to restricting the places you access the current user to make your
UI more testable and modular. Read more about this in the UI article.

On the server: this.userId


On the server, each connection has a different logged in user, so there is no global logged-in
user state by definition. Since Meteor tracks the environment for each Method call, you can
still use the Meteor.userId() global, which returns a different value depending on which
Method you call it from, but you can run into edge cases when dealing with asynchronous
code. Also, Meteor.userId() won't work inside publications.

We suggest using the this.userId property on the context of Methods and publications
instead, and passing that around through function arguments to wherever you need it.

// Accessing this.userId inside a publication


Meteor.publish('lists.private', function() {
if (!this.userId) {
return this.ready();
}

return Lists.find({
userId: this.userId
}, {
fields: Lists.publicFields
});
});

107
Users and Accounts

// Accessing this.userId inside a Method


Meteor.methods({
'todos.updateText'({ todoId, newText }) {
new SimpleSchema({
todoId: { type: String },
newText: { type: String }
}).validate({ todoId, newText }),

const todo = Todos.findOne(todoId);

if (!todo.editableBy(this.userId)) {
throw new Meteor.Error('todos.updateText.unauthorized',
'Cannot edit todos in a private list that is not yours');
}

Todos.update(todoId, {
$set: { text: newText }
});
}
});

The Meteor.users collection


Meteor comes with a default MongoDB collection for user data. It's stored in the database
under the name users , and is accessible in your code through Meteor.users . The schema
of a user document in this collection will depend on which login service was used to create
the account. Here's an example of a user that created their account with accounts-password :

108
Users and Accounts

{
"_id": "DQnDpEag2kPevSdJY",
"createdAt": "2015-12-10T22:34:17.610Z",
"services": {
"password": {
"bcrypt": "XXX"
},
"resume": {
"loginTokens": [
{
"when": "2015-12-10T22:34:17.615Z",
"hashedToken": "XXX"
}
]
}
},
"emails": [
{
"address": "ada@lovelace.com",
"verified": false
}
]
}

Here's what the same user would look like if they instead logged in with Facebook:

109
Users and Accounts

{
"_id": "Ap85ac4r6Xe3paeAh",
"createdAt": "2015-12-10T22:29:46.854Z",
"services": {
"facebook": {
"accessToken": "XXX",
"expiresAt": 1454970581716,
"id": "XXX",
"email": "ada@lovelace.com",
"name": "Ada Lovelace",
"first_name": "Ada",
"last_name": "Lovelace",
"link": "https://www.facebook.com/app_scoped_user_id/XXX/",
"gender": "female",
"locale": "en_US",
"age_range": {
"min": 21
}
},
"resume": {
"loginTokens": [
{
"when": "2015-12-10T22:29:46.858Z",
"hashedToken": "XXX"
}
]
}
},
"profile": {
"name": "Sashko Stubailo"
}
}

Note that the schema is different when users register with different login services. There are
a few things to be aware of when dealing with this collection:

1. User documents in the database have secret data like access keys and hashed
passwords. When publishing user data to the client, be extra careful not to include
anything that client shouldn't be able to see.
2. DDP, Meteor's data publication protocol, only knows how to resolve conflicts in top-level
fields. This means that you can't have one publication send
services.facebook.first_name and another send services.facebook.locale - one of

them will win, and only one of the fields will actually be available on the client. The best
way to fix this is to denormalize the data you want onto custom top-level fields, as
described in the section about custom user data.
3. The OAuth login service packages populate profile.name . We don't recommend using
this but, if you plan to, make sure to deny client-side writes to profile . See the section

110
Users and Accounts

about the profile field on users.


4. When finding users by email or username, make sure to use the case-insensitive
functions provided by accounts-password . See the section about case-sensitivity for
more details.

Custom data about users


As your app gets more complex, you will invariably need to store some data about individual
users, and the most natural place to put that data is in additional fields on the Meteor.users
collection described above. In a more normalized data situation it would be a good idea to
keep Meteor's user data and yours in two separate tables, but since MongoDB doesn't deal
well with data associations it makes sense to just use one collection.

Add top-level fields onto the user document


The best way to store your custom data onto the Meteor.users collection is to add a new
uniquely-named top-level field on the user document. For example, if you wanted to add a
mailing address to a user, you could do it like this:

// Using address schema from schema.org


// https://schema.org/PostalAddress
const newMailingAddress = {
addressCountry: 'US',
addressLocality: 'Seattle',
addressRegion: 'WA',
postalCode: '98052',
streetAddress: "20341 Whitworth Institute 405 N. Whitworth"
};

Meteor.users.update(userId, {
$set: {
mailingAddress: newMailingAddress
}
});

You can use any field name other than those used by the Accounts sytem.

Adding fields on user registration


The code above is just code that you could run on the server inside a Meteor Method to set
someone's mailing address. Sometimes, you want to set a field when the user first creates
their account, for example to initialize a default value or compute something from their social
data. You can do this using Accounts.onCreateUser :

111
Users and Accounts

// Generate user initials after Facebook login


Accounts.onCreateUser((options, user) => {
if (! user.services.facebook) {
throw new Error('Expected login with Facebook only.');
}

const { first_name, last_name } = user.services.facebook;


user.initials = first_name[0].toUpperCase() + last_name[0].toUpperCase();

// Don't forget to return the new user object at the end!


return user;
});

Note that the user object provided doesn't have an _id field yet. If you need to do
something with the new user's ID inside this function, a useful trick can be to generate the ID
yourself:

// Generate a todo list for each new user


Accounts.onCreateUser((options, user) => {
// Generate a user ID ourselves
user._id = Random.id(); // Need to add the `random` package

// Use the user ID we generated


Lists.createListForUser(user._id);

// Don't forget to return the new user object at the end!


return user;
});

Don't use profile


There's a tempting existing field called profile that is added by default when a new user
registers. This field was historically intended to be used as a scratch pad for user-specific
data - maybe their image avatar, name, intro text, etc. Because of this, the profile field
on every user is automatically writeable by that user from the client. It's also
automatically published to the client for that particular user.

It turns out that having a field writeable by default without making that super obvious might
not be the best idea. There are many stories of new Meteor developers storing fields such
as isAdmin on profile ... and then a malicious user can easily set that to true whenever
they want, making themselves an admin. Even if you aren't concerned about this, it isn't a
good idea to let malicious users store arbitrary amounts of data in your database.

Rather than dealing with the specifics of this field, it can be helpful to just ignore its existence
entirely. You can safely do that as long as you deny all writes from the client:

112
Users and Accounts

// Deny all client-side updates to user documents


Meteor.users.deny({
update() { return true; }
});

Even ignoring the security implications of profile , it isn't a good idea to put all of your
app's custom data onto one field. As discussed in the Collections article, Meteor's data
transfer protocol doesn't do deeply nested diffing of fields, so it's a good idea to flatten out
your objects into many top-level fields on the document.

Publishing custom data


If you want to access the custom data you've added to the Meteor.users collection in your
UI, you'll need to publish it to the client. Mostly, you can just follow the advice in the Data
Loading and Security articles.

The most important thing to keep in mind is that user documents are certain to contain
private data about your users. In particular, the user document includes hashed password
data and access keys for external APIs. This means it's critically important to filter the fields
of the user document that you send to any client.

Note that in Meteor's publication and subscription system, it's totally fine to publish the same
document multiple times with different fields - they will get merged internally and the client
will see a consistent document with all of the fields together. So if you just added one custom
field, you should just write a publication with that one field. Let's look at an example of how
we might publish the initials field from above:

Meteor.publish('Meteor.users.initials', function ({ userIds }) {


// Validate the arguments to be what we expect
new SimpleSchema({
userIds: { type: [String] }
}).validate({ userIds });

// Select only the users that match the array of IDs passed in
const selector = {
_id: { $in: userIds }
};

// Only return one field, `initials`


const options = {
fields: { initials: 1 }
};

return Meteor.users.find(selector, options);


});

113
Users and Accounts

This publication will let the client pass an array of user IDs it's interested in, and get the
initials for all of those users.

Roles and permissions


One of the main reasons you might want to add a login system to your app is to have
permissions for your data. For example, if you were running a forum, you would want
administrators or moderators to be able to delete any post, but normal users can only delete
their own. This uncovers two different types of permissions:

1. Role-based permissions
2. Per-document permissions

alanning:roles
The most popular package for role-based permissions in Meteor is alanning:roles . For
example, here is how you would make a user into an administrator, or a moderator:

// Give Alice the 'admin' role


Roles.addUsersToRoles(aliceUserId, 'admin', Roles.GLOBAL_GROUP);

// Give Bob the 'moderator' role for a particular category


Roles.addUsersToRoles(bobsUserId, 'moderator', categoryId);

Now, let's say you wanted to check if someone was allowed to delete a particular forum
post:

const forumPost = Posts.findOne(postId);

const canDelete = Roles.userIsInRole(userId,


['admin', 'moderator'], forumPost.categoryId);

if (! canDelete) {
throw new Meteor.Error('unauthorized',
'Only admins and moderators can delete posts.');
}

Posts.remove(postId);

Note that we can check for multiple roles at once, and if someone has a role in the
GLOBAL_GROUP , they are considered as having that role in every group. In this case, the

groups were by category ID, but you could use any unique identifier to make a group.

Read more in the alanning:roles package documentation.

114
Users and Accounts

Per-document permissions
Sometimes, it doesn't make sense to abstract permissions into "groups" - you just want
documents to have owners and that's it. In this case, you can use a simpler strategy using
collection helpers.

Lists.helpers({
// ...
editableBy(userId) {
if (!this.userId) {
return true;
}

return this.userId === userId;


},
// ...
});

Now, we can call this simple function to determine if a particular user is allowed to edit this
list:

const list = Lists.findOne(listId);

if (! list.editableBy(userId)) {
throw new Meteor.Error('unauthorized',
'Only list owners can edit private lists.');
}

Learn more about how to use collection helpers in the Collections article.

115
Testing

Introduction
Testing allows you to ensure your application works the way you think it does, especially as
your codebase changes over time. If you have good tests, you can refactor and rewrite code
with confidence. Tests are also the most concrete form of documentation of expected
behavior, since other developers can figure out how to use your code by reading the tests.

Automated testing is critical because it allows you to run a far greater set of tests much more
often than you could manually, allowing you to catch regression errors immediately.

Types of tests
Entire books have been written on the subject of testing, so we will simply touch on some
basics of testing here. The important thing to consider when writing a test is what part of the
application you are trying to test, and how you are verifying the behavior works.

Unit test: If you are testing one small module of your application, you are writing a unit
test. You'll need to stub and mock other modules that your module usually leverages in
order to isolate each test. You'll typically also need to spy on actions that the module
takes to verify that they occur.

Integration test: If you are testing that multiple modules behave properly in concert,
you are writing an integration test. Such tests are much more complex and may require
running code both on the client and on the server to verify that communication across
that divide is working as expected. Typically an integration test will still isolate a part of
the entire application and directly verify results in code.

Acceptance test: If you want to write a test that can be run against any running version
of your app and verifies at the browser level that the right things happen when you push
the right buttons, then you are writing an acceptance test (sometimes called "end to end
test"). Such tests typically try to hook into the application as little as possible, beyond
perhaps setting up the right data to run a test against.

Load test: Finally you may wish to test that your application works under typical load or
see how much load it can handle before it falls over. This is called a load test or stress
test. Such tests can be challenging to set up and typically aren't run often but are very
important for confidence before a big production launch.

Challenges of testing in Meteor

116
Testing

In most ways, testing a Meteor app is no different from testing any other full stack JavaScript
application. However, compared to more traditional backend or front-end focused
frameworks, two factors can make testing a little more challenging:

Client/server data: Meteor's data system makes it simple to bridge the client-server
gap and often allows you to build your application without thinking about how data
moves around. It becomes critical to test that your code does actually work correctly
across that gap. In traditional frameworks where you spend a lot of time thinking about
interfaces between client and server you can often get away with testing both sides of
the interface in isolation, but Meteor's full app test mode makes it easy to write
integration tests that cover the full stack. Another challenge here is creating test data in
the client context; we'll discuss ways to do this in the section on generating test data
below.

Reactivity: Meteor's reactivity system is "eventually consistent" in the sense that when
you change a reactive input to the system, you'll see the user interface change to reflect
this some time later. This can be a challenge when testing, but there are some ways to
wait until those changes happen to verify the results, for example
Tracker.afterFlush() .

The 'meteor test' command


The primary way to test your application in Meteor is the meteor test command.

This loads your application in a special "test mode". What this does is:

1. Doesn't eagerly load any of our application code as Meteor normally would.
2. Does eagerly load any file in our application (including in imports/ folders) that look
like *.test[s].* , or *.spec[s].*
3. Sets the Meteor.isTest flag to be true.
4. Starts up the test driver package (see below).

The Meteor build tool and the meteor test command ignore any files located in any
tests/ directory. This allows you to put tests in this directory that you can run using a

test runner outside of Meteor's built-in test tools and still not have those files loaded in
your application. See Meteor's default file load order rules.

What this means is that you can write tests in files with a certain filename pattern and know
they'll not be included in normal builds of your app. When your app runs in test mode, those
files will be loaded (and nothing else will), and they can import the modules you want to test.
As we'll see this is ideal for unit tests and simple integration tests.

117
Testing

Additionally, Meteor offers a "full application" test mode. You can run this with meteor test -
-full-app .

This is similar to test mode, with key differences:

1. It loads test files matching *.app-test[s].* and *.app-spec[s].* .


2. It does eagerly load our application code as Meteor normally would.
3. Sets the Meteor.isAppTest flag to be true (instead of the Meteor.isTest flag).

This means that the entirety of your application (including for instance the web server and
client side router) is loaded and will run as normal. This enables you to write much more
complex integration tests and also load additional files for acceptance tests.

Note that there is another test command in the Meteor tool; meteor test-packages is a way
of testing Atmosphere packages, which is discussed in the Writing Packages article.

Driver packages
When you run a meteor test command, you must provide a --driver-package argument. A
test driver is a mini-application that runs in place of your app and runs each of your defined
tests, whilst reporting the results in some kind of user interface.

There are two main kinds of test driver packages:

Web reporters: Meteor applications that display a special test reporting web UI that you
can view the test results in.

118
Testing

Console reporters: These run completely on the command-line and are primary used
for automated testing like continuous integration (as we'll see, typically PhantomJS is
used to drive such tests).

Recommended: Mocha
In this article, we'll use the popular Mocha test runner alongside the Chai assertion library to
test our application. In order to write and run tests in Mocha, we need to add an appropriate
test driver package.

There are several options. Choose the ones that makes sense for your app. You may
depend on more than one and set up different test commands for different situations.

practicalmeteor:mocha Runs client and server package or app tests and displays all
results in a browser. Use spacejam for command line / CI support.
dispatch:mocha-phantomjs Runs client and server package or app tests using
PhantomJS and reports all results in the server console. Can be used for running tests
on a CI server. Has a watch mode.

119
Testing

dispatch:mocha-browser Runs client and server package or app tests with Mocha
reporting client results in a web browser and server results in the server console. Has a
watch mode.
dispatch:mocha Runs server-only package or app tests with Mocha and reports all
results in the server console. Can be used for running tests on a CI server. Has a watch
mode.

These packages don't do anything in development or production mode. They declare


themselves testOnly so they are not even loaded outside of testing. But when our app is
run in test mode, the test driver package takes over, executing test code on both the client
and server, and rendering results to the browser.

Here's how we can add the practicalmeteor:mocha package to our app:

meteor add practicalmeteor:mocha

Test Files
Test files themselves (for example a file named todos-item.test.js or routing.app-
specs.coffee ) can register themselves to be run by the test driver in the usual way for that

testing library. For Mocha, that's by using describe and it :

describe('my module', function () {


it('does something that should be tested', function () {
// This code will be executed by the test driver when the app is started
// in the correct mode
})
})

Note that arrow function use with Mocha is discouraged.

Test data
When your app is run in test mode, it is initialized with a clean test database.

If you are running a test that relies on using the database, and specifically the content of the
database, you'll need to perform some setup steps in your test to ensure the database is in
the state you expect. There are some tools you can use to do this.

To ensure the database is clean, the xolvio:cleaner package is useful. You can use it to
reset the database in a beforeEach block:

120
Testing

import { resetDatabase } from 'meteor/xolvio:cleaner';

describe('my module', function () {


beforeEach(function () {
resetDatabase();
});
});

This technique will only work on the server. If you need to reset the database from a client
test, you can use a method to do so:

import { resetDatabase } from 'meteor/xolvio:cleaner';

// NOTE: Before writing a method like this you'll want to double check
// that this file is only going to be loaded in test mode!!
Meteor.methods({
'test.resetDatabase': () => resetDatabase(),
});

describe('my module', function (done) {


beforeEach(function (done) {
// We need to wait until the method call is done before moving on, so we
// use Mocha's async mechanism (calling a done callback)
Meteor.call('test.resetDatabase', done);
});
});

As we've placed the code above in a test file, it will not load in normal development or
production mode (which would be an incredibly bad thing!). If you create a Atmosphere
package with a similar feature, you should mark it as testOnly and it will similarly only load
in test mode.

Generating test data


Often it's sensible to create a set of data to run your test against. You can use standard
insert() calls against your collections to do this, but often it's easier to create factories

which help encode random test data. A great package to use to do this is dburles:factory .

In the Todos example app, we define a factory to describe how to create a test todo item,
using the faker npm package:

121
Testing

import faker from 'faker';

Factory.define('todo', Todos, {
listId: () => Factory.get('list'),
text: () => faker.lorem.sentence(),
createdAt: () => new Date(),
});

To use the factory in a test, we simply call Factory.create :

// This creates a todo and a list in the database and returns the todo.
const todo = Factory.create('todo');

// If we have a list already, we can pass in the id and avoid creating another:
const list = Factory.create('list');
const todoInList = Factory.create('todo', { listId: list._id });

Mocking the database


As Factory.create directly inserts documents into the collection that's passed into the
Factory.define function, it can be a problem to use it on the client. However there's a neat

isolation trick that you can do to replace the server-backed Todos client collection with a
mocked out local collection, that's encoded in the hwillson:stub-collections package.

import StubCollections from 'meteor/hwillson:stub-collections';


import { Todos } from 'path/to/todos.js';

StubCollections.stub(Todos);

// Now Todos is stubbed to a simple local collection mock,


// so for instance on the client we can do:
Todos.insert({ a: 'document' });

// Restore the `Todos` collection


StubCollections.restore();

In a Mocha test, it makes sense to use stub-collections in a beforeEach / afterEach


block.

Unit testing

122
Testing

Unit testing is the process of isolating a section of code and then testing that the internals of
that section work as you expect. As we've split our code base up into ES2015 modules it's
natural to test those modules one at a time.

By isolating a module and simply testing its internal functionality, we can write tests that are
fast and accurate---they can quickly tell you where a problem in your application lies. Note
however that incomplete unit tests can often hide bugs because of the way they stub out
dependencies. For that reason it's useful to combine unit tests with slower (and perhaps less
commonly run) integration and acceptance tests.

A simple Blaze unit test


In the Todos example app, thanks to the fact that we've split our User Interface into smart
and reusable components, it's natural to want to unit test some of our reusable components
(we'll see below how to integration test our smart components).

To do so, we'll use a very simple test helper that renders a Blaze component off-screen with
a given data context. As we place it in imports , it won't load in our app by in normal mode
(as it's not required anywhere).

imports/ui/test-helpers.js :

import { _ } from 'meteor/underscore';


import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze';
import { Tracker } from 'meteor/tracker';

const withDiv = function withDiv(callback) {


const el = document.createElement('div');
document.body.appendChild(el);
try {
callback(el);
} finally {
document.body.removeChild(el);
}
};

export const withRenderedTemplate = function withRenderedTemplate(template, data, call


back) {
withDiv((el) => {
const ourTemplate = _.isString(template) ? Template[template] : template;
Blaze.renderWithData(ourTemplate, data, el);
Tracker.flush();
callback(el);
});
};

123
Testing

A simple example of a reusable component to test is the Todos_item template. Here's what
a unit test looks like (you can see some others in the app repository).

imports/ui/components/client/todos-item.tests.js :

/* eslint-env mocha */
/* eslint-disable func-names, prefer-arrow-callback */

import { Factory } from 'meteor/factory';


import { chai } from 'meteor/practicalmeteor:chai';
import { Template } from 'meteor/templating';
import { $ } from 'meteor/jquery';

import { withRenderedTemplate } from '../../test-helpers.js';


import '../todos-item.js';

describe('Todos_item', function () {
beforeEach(function () {
Template.registerHelper('_', key => key);
});

afterEach(function () {
Template.deregisterHelper('_');
});

it('renders correctly with simple data', function () {


const todo = Factory.build('todo', { checked: false });
const data = {
todo,
onEditingChange: () => 0,
};

withRenderedTemplate('Todos_item', data, el => {


chai.assert.equal($(el).find('input[type=text]').val(), todo.text);
chai.assert.equal($(el).find('.list-item.checked').length, 0);
chai.assert.equal($(el).find('.list-item.editing').length, 0);
});
});
});

Of particular interest in this test is the following:

Importing
When we run our app in test mode, only our test files will be eagerly loaded. In particular,
this means that in order to use our templates, we need to import them! In this test, we import
todos-item.js , which itself imports todos.html (yes, you do need to import the HTML files

of your Blaze templates!)

124
Testing

Stubbing
To be a unit test, we must stub out the dependencies of the module. In this case, thanks to
the way we've isolated our code into a reusable component, there's not much to do;
principally we need to stub out the {% raw %}{{_}}{% endraw %} helper that's created by the
tap:i18n system. Note that we stub it out in a beforeEach and restore it the afterEach .

If you're testing code that makes use of globals, you'll need to import those globals. For
instance if you have a global Todos collection and are testing this file:

// logging.js
export function logTodos() {
console.log(Todos.findOne());
}

then you'll need to import Todos both in that file and in the test:

// logging.js
import { Todos } from './todos.js'
export function logTodos() {
console.log(Todos.findOne());
}

// logging.test.js
import { Todos } from './todos.js'
Todos.findOne = () => {
return {text: "write a guide"}
}

import { logTodos } from './logging.js'


// then test logTodos
...

Creating data
We can use the Factory package's .build() API to create a test document without
inserting it into any collection. As we've been careful not to call out to any collections directly
in the reusable component, we can pass the built todo document directly into the template.

A simple React unit test

125
Testing

We can also apply the same structure to testing React components and recommend the
Enzyme package, which simulates a React component's environment and allows you to
query it using CSS selectors. A larger suite of tests is available in the react branch of the
Todos app, but let's look at a simple example for now:

import { Factory } from 'meteor/factory';


import React from 'react';
import { shallow } from 'enzyme';
import { chai } from 'meteor/practicalmeteor:chai';
import TodoItem from './TodoItem.jsx';

describe('TodoItem', () => {
it('should render', () => {
const todo = Factory.build('todo', { text: 'testing', checked: false });
const item = shallow(<TodoItem todo={todo} />);
chai.assert(item.hasClass('list-item'));
chai.assert(!item.hasClass('checked'));
chai.assert.equal(item.find('.editing').length, 0);
chai.assert.equal(item.find('input[type="text"]').prop('defaultValue'), 'testing')
;
});
});

The test is slightly simpler than the Blaze version above because the React sample app is
not internationalized. Otherwise, it's conceptually identical. We use Enzyme's shallow
function to render the TodoItem component, and the resulting object to query the document,
and also to simulate user interactions. And here's an example of simulating a user checking
the todo item:

126
Testing

import { Factory } from 'meteor/factory';


import React from 'react';
import { shallow } from 'enzyme';
import { sinon } from 'meteor/practicalmeteor:sinon';
import TodoItem from './TodoItem.jsx';
import { setCheckedStatus } from '../../api/todos/methods.js';

describe('TodoItem', () => {
it('should update status when checked', () => {
sinon.stub(setCheckedStatus, 'call');
const todo = Factory.create('todo', { checked: false });
const item = shallow(<TodoItem todo={todo} />);

item.find('input[type="checkbox"]').simulate('change', {
target: { checked: true },
});

sinon.assert.calledWith(setCheckedStatus.call, {
todoId: todo._id,
newCheckedStatus: true,
});

setCheckedStatus.call.restore();
});
});

In this case, the TodoItem component calls a Meteor Method setCheckedStatus when the
user clicks, but this is a unit test so there's no server running. So we stub it out using Sinon.
After we simulate the click, we verify that the stub was called with the correct arguments.
Finally, we clean up the stub and restore the original method behavior.

Running unit tests


To run the tests that our app defines, we run our app in test mode:

meteor test --driver-package practicalmeteor:mocha

As we've defined a test file ( imports/todos/todos.tests.js ), what this means is that the file
above will be eagerly loaded, adding the 'builds correctly from factory' test to the Mocha
registry.

To run the tests, visit http://localhost:3000 in your browser. This kicks off
practicalmeteor:mocha , which runs your tests both in the browser and on the server. It

displays the test results in the browser in a Mocha test reporter:

127
Testing

Usually, while developing an application, it makes sense to run meteor test on a second
port (say 3100 ), while also running your main application in a separate process:

# in one terminal window


meteor

# in another
meteor test --driver-package practicalmeteor:mocha --port 3100

Then you can open two browser windows to see the app in action while also ensuring that
you don't break any tests as you make changes.

Isolation techniques
In the unit tests above we saw a very limited example of how to isolate a module from the
larger app. This is critical for proper unit testing. Some other utilities and techniques include:

128
Testing

The velocity:meteor-stubs package, which creates simple stubs for most Meteor core
objects.

Alternatively, you can also use tools like Sinon to stub things directly, as we'll see for
example in our simple integration test.

The hwillson:stub-collections package we mentioned above.

There's a lot of scope for better isolation and testing utilities.

Testing publications
Using the johanbrook:publication-collector package, you're able to test individual
publication's output without needing to create a traditional subscription:

describe('lists.public', function () {
it('sends all public lists', function (done) {
// Set a user id that will be provided to the publish function as `this.userId`,
// in case you want to test authentication.
const collector = new PublicationCollector({userId: 'some-id'});

// Collect the data published from the `lists.public` publication.


collector.collect('lists.public', (collections) => {
// `collections` is a dictionary with collection names as keys,
// and their published documents as values in an array.
// Here, documents from the collection 'Lists' are published.
chai.assert.typeOf(collections.Lists, 'array');
chai.assert.equal(collections.Lists.length, 3);
done();
});
});
});

Note that user documents – ones that you would normally query with Meteor.users.find() –
will be available as the key users on the dictionary passed from a
PublicationCollector.collect() call. See the tests in the package for more details.

Integration testing
An integration test is a test that crosses module boundaries. In the simplest case, this simply
means something very similar to a unit test, where you perform your isolation around
multiple modules, creating a non-singular "system under test".

129
Testing

Although conceptually different to unit tests, such tests typically do not need to be run any
differently to unit tests and can use the same meteor test mode and isolation techniques
as we use for unit tests.

However, an integration test that crosses the client-server boundary of a Meteor application
(where the modules under test cross that boundary) requires a different testing
infrastructure, namely Meteor's "full app" testing mode.

Let's take a look at example of both kinds of tests.

Simple integration test


Our reusable components were a natural fit for a unit test; similarly our smart components
tend to require an integration test to really be exercised properly, as the job of a smart
component is to bring data together and supply it to a reusable component.

In the Todos example app, we have an integration test for the Lists_show_page smart
component. This test simply ensures that when the correct data is present in the database,
the template renders correctly -- that it is gathering the correct data as we expect. It isolates
the rendering tree from the more complex data subscription part of the Meteor stack. If we
wanted to test that the subscription side of things was working in concert with the smart
component, we'd need to write a full app integration test.

imports/ui/components/client/todos-item.tests.js :

/* eslint-env mocha */
/* eslint-disable func-names, prefer-arrow-callback */

import { Meteor } from 'meteor/meteor';


import { Factory } from 'meteor/factory';
import { Random } from 'meteor/random';
import { chai } from 'meteor/practicalmeteor:chai';
import StubCollections from 'meteor/hwillson:stub-collections';
import { Template } from 'meteor/templating';
import { _ } from 'meteor/underscore';
import { $ } from 'meteor/jquery';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { sinon } from 'meteor/practicalmeteor:sinon';

import { withRenderedTemplate } from '../../test-helpers.js';


import '../lists-show-page.js';

import { Todos } from '../../../api/todos/todos.js';


import { Lists } from '../../../api/lists/lists.js';

describe('Lists_show_page', function () {
const listId = Random.id();

130
Testing

beforeEach(function () {
StubCollections.stub([Todos, Lists]);
Template.registerHelper('_', key => key);
sinon.stub(FlowRouter, 'getParam', () => listId);
sinon.stub(Meteor, 'subscribe', () => ({
subscriptionId: 0,
ready: () => true,
}));
});

afterEach(function () {
StubCollections.restore();
Template.deregisterHelper('_');
FlowRouter.getParam.restore();
Meteor.subscribe.restore();
});

it('renders correctly with simple data', function () {


Factory.create('list', { _id: listId });
const timestamp = new Date();
const todos = _.times(3, i => Factory.create('todo', {
listId,
createdAt: new Date(timestamp - (3 - i)),
}));

withRenderedTemplate('Lists_show_page', {}, el => {


const todosText = todos.map(t => t.text).reverse();
const renderedText = $(el).find('.list-items input[type=text]')
.map((i, e) => $(e).val())
.toArray();
chai.assert.deepEqual(renderedText, todosText);
});
});
});

Of particular interest in this test is the following:

Importing
As we'll run this test in the same way that we did our unit test, we need to import the
relevant modules under test in the same way that we did in the unit test.

Stubbing
As the system under test in our integration test has a larger surface area, we need to stub
out a few more points of integration with the rest of the stack. Of particular interest here is
our use of the hwillson:stub-collections package and of Sinon to stub out Flow Router

131
Testing

and our Subscription.

Creating data
In this test, we used Factory package's .create() API, which inserts data into the real
collection. However, as we've proxied all of the Todos and Lists collection methods onto
a local collection (this is what hwillson:stub-collections is doing), we won't run into any
problems with trying to perform inserts from the client.

This integration test can be run the exact same way as we ran unit tests above.

Full-app integration test


In the Todos example application, we have a integration test which ensures that we see the
full contents of a list when we route to it, which demonstrates a few techniques of integration
tests.

imports/startup/client/routes.app-test.js :

/* eslint-env mocha */
/* eslint-disable func-names, prefer-arrow-callback */

import { Meteor } from 'meteor/meteor';


import { Tracker } from 'meteor/tracker';
import { DDP } from 'meteor/ddp-client';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { assert } from 'meteor/practicalmeteor:chai';
import { Promise } from 'meteor/promise';
import { $ } from 'meteor/jquery';

import { denodeify } from '../../utils/denodeify';


import { generateData } from './../../api/generate-data.app-tests.js';
import { Lists } from '../../api/lists/lists.js';
import { Todos } from '../../api/todos/todos.js';

// Utility -- returns a promise which resolves when all subscriptions are done
const waitForSubscriptions = () => new Promise(resolve => {
const poll = Meteor.setInterval(() => {
if (DDP._allSubscriptionsReady()) {
Meteor.clearInterval(poll);
resolve();
}
}, 200);
});

// Tracker.afterFlush runs code when all consequent of a tracker based change


// (such as a route change) have occured. This makes it a promise.

132
Testing

const afterFlushPromise = denodeify(Tracker.afterFlush);

if (Meteor.isClient) {
describe('data available when routed', () => {
// First, ensure the data that we expect is loaded on the server
// Then, route the app to the homepage
beforeEach(() => generateData()
.then(() => FlowRouter.go('/'))
.then(waitForSubscriptions)
);

describe('when logged out', () => {


it('has all public lists at homepage', () => {
assert.equal(Lists.find().count(), 3);
});

it('renders the correct list when routed to', () => {


const list = Lists.findOne();
FlowRouter.go('Lists.show', { _id: list._id });

return afterFlushPromise()
.then(waitForSubscriptions)
.then(() => {
assert.equal($('.title-wrapper').html(), list.name);
assert.equal(Todos.find({ listId: list._id }).count(), 3);
});
});
});
});
}

Of note here:

Before running, each test sets up the data it needs using the generateData helper (see
the section on creating integration test data for more detail) then goes to the homepage.

Although Flow Router doesn't take a done callback, we can use Tracker.afterFlush to
wait for all its reactive consequences to occur.

Here we wrote a little utility (which could be abstracted into a general package) to wait
for all the subscriptions which are created by the route change (the todos.inList
subscription in this case) to become ready before checking their data.

Running full-app tests


To run the full-app tests in our application, we run:

meteor test --full-app --driver-package practicalmeteor:mocha

133
Testing

When we connect to the test instance in a browser, we want to render a testing UI rather
than our app UI, so the mocha-web-reporter package will hide any UI of our application and
overlay it with its own. However the app continues to behave as normal, so we are able to
route around and check the correct data is loaded.

Creating data
To create test data in full-app test mode, it usually makes sense to create some special test
methods which we can call from the client side. Usually when testing a full app, we want to
make sure the publications are sending through the correct data (as we do in this test), and
so it's not sufficient to stub out the collections and place synthetic data in them. Instead we'll
want to actually create data on the server and let it be published.

Similar to the way we cleared the database using a method in the beforeEach in the test
data section above, we can call a method to do that before running our tests. In the case of
our routing tests, we've used a file called imports/api/generate-data.app-tests.js which
defines this method (and will only be loaded in full app test mode, so is not available in
general!):

134
Testing

// This file will be auto-imported in the app-test context,


// ensuring the method is always available

import { Meteor } from 'meteor/meteor';


import { Factory } from 'meteor/factory';
import { resetDatabase } from 'meteor/xolvio:cleaner';
import { Random } from 'meteor/random';
import { _ } from 'meteor/underscore';

import { denodeify } from '../utils/denodeify';

const createList = (userId) => {


const list = Factory.create('list', { userId });
_.times(3, () => Factory.create('todo', { listId: list._id }));
return list;
};

// Remember to double check this is a test-only file before


// adding a method like this!
Meteor.methods({
generateFixtures() {
resetDatabase();

// create 3 public lists


_.times(3, () => createList());

// create 3 private lists


_.times(3, () => createList(Random.id()));
},
});

let generateData;
if (Meteor.isClient) {
// Create a second connection to the server to use to call
// test data methods. We do this so there's no contention
// with the currently tested user's connection.
const testConnection = Meteor.connect(Meteor.absoluteUrl());

generateData = denodeify((cb) => {


testConnection.call('generateFixtures', cb);
});
}

export { generateData };

Note that we've exported a client-side symbol generateData which is a promisified version
of the method call, which makes it simpler to use this sequentially in tests.

Also of note is the way we use a second DDP connection to the server in order to send
these test "control" method calls.

135
Testing

Acceptance testing
Acceptance testing is the process of taking an unmodified version of our application and
testing it from the "outside" to make sure it behaves in a way we expect. Typically if an app
passes acceptance tests, we have done our job properly from a product perspective.

As acceptance tests test the behavior of the application in a full browser context in a generic
way, there are a range of tools that you can use to specify and run such tests. In this guide
we'll demonstrate using Chimp, an acceptance testing tool with a few neat Meteor-specific
features that makes it easy to use.

Chimp requires node version 4 or 5. You can check your node version by running:

node -v

You can install version 4 from nodejs.org or version 5 with brew install node . Then we can
install the Chimp tool globally using:

npm install --global chimp

Note that you can also install Chimp as a devDependency in your package.json but you
may run into problems deploying your application as it includes binary dependencies.
You can avoid such problems by running meteor npm prune to remove non-production
dependencies before deploying.

Chimp has a variety of options for setting it up, but we can add some npm scripts which will
run the currently tests we define in Chimp's two main modes. We can add them to our
package.json :

{
"scripts": {
"chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests",
"chimp-test": "chimp --mocha --path=tests"
}
}

Chimp will now look in the tests/ directory (otherwise ignored by the Meteor tool) for files
in which you define acceptance tests. In the Todos example app, we define a simple test that
ensures we can click the "create list" button.

tests/lists.js :

136
Testing

/* eslint-env mocha */
/* eslint-disable func-names, prefer-arrow-callback */

// These are Chimp globals


/* globals browser assert server */

function countLists() {
browser.waitForExist('.list-todo');
const elements = browser.elements('.list-todo');
return elements.value.length;
};

describe('list ui', function () {


beforeEach(function () {
browser.url('http://localhost:3000');
server.call('generateFixtures');
});

it('can create a list @watch', function () {


const initialCount = countLists();

browser.click('.js-new-list');

assert.equal(countLists(), initialCount + 1);


});
});

Running acceptance tests


To run acceptance tests, we simply need to start our Meteor app as usual, and point Chimp
at it.

In one terminal, we can do:

meteor

In another:

meteor npm run chimp-watch

The chimp-watch command will then run the test in a browser, and continue to re-run it as
we change the test or the application. (Note that the test assumes we are running the app on
port 3000 ).

137
Testing

Thus it's a good way to develop the test---this is why chimp has a feature where we mark
tests with a @watch in the name to call out the tests we want to work on (running our entire
acceptance test suite can be time consuming in a large application).

The chimp-test command will run all of the tests once only and is good for testing that our
suite passes, either as a manual step, or as part of a continuous integration process.

Creating data
Although we can run the acceptance test against our "pure" Meteor app, as we've done
above, it often makes sense to start our meteor server with a special test driver,
tmeasday:acceptance-test-driver . (You'll need to meteor add it to your app):

meteor test --full-app --driver-package tmeasday:acceptance-test-driver

The advantage of running our acceptance test suite pointed at an app that runs in full app
test mode is that all of the data generating methods that we've created remain available.
Otherwise the acceptance-test-driver does nothing.

In Chimp tests, you have a DDP connection to the server available on the server variable.
You can thus use server.call() (which is wrapped to be synchronous in Chimp tests) to
call these methods. This is a convenient way to share data preparation code between
acceptance and integration tests.

Continuous Integration
Continuous integration testing is the process of running tests on every commit of your
project.

There are two principal ways to do it: on the developer's machine before allowing them to
push code to the central repository, and on a dedicated CI server after each push. Both
techniques are useful, and both require running tests in a commandline-only fashion.

Command line
We've seen one example of running tests on the command line, using our meteor npm run
chimp-test mode.

We can also use a command-line driver for Mocha dispatch:mocha-phantomjs to run our
standard tests on the command line.

Adding and using the package is straightforward:

138
Testing

meteor add dispatch:mocha-phantomjs


meteor test --once --driver-package dispatch:mocha-phantomjs

(The --once argument ensures the Meteor process stops once the test is done).

We can also add that command to our package.json as a test script:

{
"scripts": {
"test": "meteor test --once --driver-package dispatch:mocha-phantomjs"
}
}

Now we can run the tests with meteor npm test .

CircleCI
CircleCI is a great continuous integration service that allows us to run (possibly time
consuming) tests on every push to a repository like GitHub. To use it with the commandline
test we've defined above, we can follow their standard getting started tutorial and use a
circle.yml file similar to this:

machine:
node:
version: 0.10.43
dependencies:
override:
- curl https://install.meteor.com | /bin/sh
- npm install
checkout:
post:
- git submodule update --init

139
URLs and Routing

After reading this guide, you'll know:

1. The role URLs play in a client-rendered app, and how it's different from a traditional
server-rendered app.
2. How to define client and server routes for your app using Flow Router.
3. How to have your app display different content depending on the URL.
4. How to construct links to routes and go to routes programmatically.

Client-side Routing
In a web application, routing is the process of using URLs to drive the user interface (UI).
URLs are a prominent feature in every single web browser, and have several main functions
from the user's point of view:

1. Bookmarking - Users can bookmark URLs in their web browser to save content they
want to come back to later.
2. Sharing - Users can share content with others by sending a link to a certain page.
3. Navigation - URLs are used to drive the web browser's back/forward functions.

In a traditional web application stack, where the server renders HTML one page at a time,
the URL is the fundamental entry point for the user to access the application. Users navigate
an application by clicking through URLs, which are sent to the server via HTTP, and the
server responds appropriately via a server-side router.

In contrast, Meteor operates on the principle of data on the wire, where the server doesn’t
think in terms of URLs or HTML pages. The client application communicates with the server
over DDP. Typically as an application loads, it initializes a series of subscriptions which fetch
the data required to render the application. As the user interacts with the application,
different subscriptions may load, but there’s no technical need for URLs to be involved in this
process - you could easily have a Meteor app where the URL never changes.

However, most of the user-facing features of URLs listed above are still relevant for typical
Meteor applications. Since the server is not URL-driven, the URL just becomes a useful
representation of the client-side state the user is currently looking at. However, unlike in a
server-rendered application, it does not need to describe the entirety of the user’s current
state; it simply needs to contain the parts that you want to be linkable. For example, the URL
should contain any search filters applied on a page, but not necessarily the state of a
dropdown menu or popup.

Using Flow Router

140
URLs and Routing

To add routing to your app, install the kadira:flow-router package:

meteor add kadira:flow-router

Flow Router is a community routing package for Meteor. At the time of writing this guide, it is
at version 2.x. For detailed information about all of the features Flow Router has to offer,
refer to the Kadira Meteor routing guide.

Defining a simple route


The basic purpose of a router is to match certain URLs and perform actions as a result. This
all happens on the client side, in the app user's browser or mobile app container. Let's take
an example from the Todos example app:

FlowRouter.route('/lists/:_id', {
name: 'Lists.show',
action(params, queryParams) {
console.log("Looking at a list?");
}
});

This route handler will run in two situations: if the page loads initially at a URL that matches
the URL pattern, or if the URL changes to one that matches the pattern while the page is
open. Note that, unlike in a server-side-rendered app, the URL can change without any
additional requests to the server.

When the route is matched, the action method executes, and you can perform any actions
you need to. The name property of the route is optional, but will let us refer to this route
more conveniently later on.

URL pattern matching


Consider the following URL pattern, used in the code snippet above:

'/lists/:_id'

The above pattern will match certain URLs. You may notice that one segment of the URL is
prefixed by : - this means that it is a url parameter, and will match any string that is
present in that segment of the path. Flow Router will make that part of the URL available on
the params property of the current route.

141
URLs and Routing

Additionally, the URL could contain an HTTP query string (the part after an optional ? ). If
so, Flow Router will also split it up into named parameters, which it calls queryParams .

Here are some example URLs and the resulting params and queryParams :

matches
URL params queryParams
pattern?
/ no

/about no
/lists/ no
{ _id:
/lists/eMtGij5AFESbTKfkT yes { }
"eMtGij5AFESbTKfkT"}
/lists/1 yes { _id: "1"} { }
{ todoSort:
/lists/1?todoSort=top yes { _id: "1"}
"top" }

Note that all of the values in params and queryParams are always strings since URLs don't
have any way of encoding data types. For example, if you wanted a parameter to represent
a number, you might need to use parseInt(value, 10) to convert it when you access it.

Accessing Route information


In addition to passing in the parameters as arguments to the action function on the route,
Flow Router makes a variety of information available via (reactive and otherwise) functions
on the global singleton FlowRouter . As the user navigates around your app, the values of
these functions will change (reactively in some cases) correspondingly.

Like any other global singleton in your application (see the data loading for info about
stores), it's best to limit your access to FlowRouter . That way the parts of your app will
remain modular and more independent. In the case of FlowRouter , it's best to access it
solely from the top of your component hierarchy, either in the "page" component, or the
layouts that wrap it. Read more about accessing data in the UI article.

The current route


It's useful to access information about the current route in your code. Here are some
reactive functions you can call:

FlowRouter.getRouteName() gets the name of the route

FlowRouter.getParam(paramName) returns the value of a single URL parameter

FlowRouter.getQueryParam(paramName) returns the value of a single URL query

142
URLs and Routing

parameter

In our example of the list page from the Todos app, we access the current list's id with
FlowRouter.getParam('_id') (we'll see more on this below).

Highlighting the active route


One situation where it is sensible to access the global FlowRouter singleton to access the
current route's information deeper in the component hierarchy is when rendering links via a
navigation component. It's often required to highlight the "active" route in some way (this is
the route or section of the site that the user is currently looking at).

A convenient package for this is zimme:active-route :

meteor add zimme:active-route

In the Todos example app, we link to each list the user knows about in the App_body
template:

{{#each list in lists}}


<a class="list-todo {{activeListClass list}}">
...

{{list.name}}
</a>
{{/each}}

We can determine if the user is currently viewing the list with the activeListClass helper:

Template.App_body.helpers({
activeListClass(list) {
const active = ActiveRoute.name('Lists.show')
&& FlowRouter.getParam('_id') === list._id;

return active && 'active';


}
});

Rendering based on the route


Now we understand how to define routes and access information about the current route, we
are in a position to do what you usually want to do when a user accesses a route---render a
user interface to the screen that represents it.

143
URLs and Routing

In this section, we'll discuss how to render routes using Blaze as the UI engine. If you are
building your app with React or Angular, you will end up with similar concepts but the code
will be a bit different.

When using Flow Router, the simplest way to display different views on the page for different
URLs is to use the complementary Blaze Layout package. First, make sure you have the
Blaze Layout package installed:

meteor add kadira:blaze-layout

To use this package, we need to define a "layout" component. In the Todos example app,
that component is called App_body :

<template name="App_body">
...
{{> Template.dynamic template=main}}
...
</template>

(This is not the entire App_body component, but we highlight the most important part here).
Here, we are using a Blaze feature called Template.dynamic to render a template which is
attached to the main property of the data context. Using Blaze Layout, we can change that
main property when a route is accessed.

We do that in the action function of our Lists.show route definition:

FlowRouter.route('/lists/:_id', {
name: 'Lists.show',
action() {
BlazeLayout.render('App_body', {main: 'Lists_show_page'});
}
});

What this means is that whenever a user visits a URL of the form /lists/X , the
Lists.show route will kick in, triggering the BlazeLayout call to set the main property of

the App_body component.

Components as pages
Notice that we called the component to be rendered Lists_show_page (rather than
Lists_show ). This indicates that this template is rendered directly by a Flow Router action

and forms the 'top' of the rendering hierarchy for this URL.

144
URLs and Routing

The Lists_show_page template renders without arguments---it is this template's


responsibility to collect information from the current route, and then pass this information
down into its child templates. Correspondingly the Lists_show_page template is very tied to
the route that rendered it, and so it needs to be a smart component. See the article on UI/UX
for more about smart and reusable components.

It makes sense for a "page" smart component like Lists_show_page to:

1. Collect route information,


2. Subscribe to relevant subscriptions,
3. Fetch the data from those subscriptions, and
4. Pass that data into a sub-component.

In this case, the HTML template for Lists_show_page will look very simple, with most of the
logic in the JavaScript code:

<template name="Lists_show_page">
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
</template>

(The {% raw %}{{#each listId in listIdArray}}{% endraw %}} is an animation technique for
page to page transitions).

145
URLs and Routing

Template.Lists_show_page.helpers({
// We use #each on an array of one item so that the "list" template is
// removed and a new copy is added when changing lists, which is
// important for animation purposes.
listIdArray() {
const instance = Template.instance();
const listId = instance.getListId();
return Lists.findOne(listId) ? [listId] : [];
},
listArgs(listId) {
const instance = Template.instance();
return {
todosReady: instance.subscriptionsReady(),
// We pass `list` (which contains the full list, with all fields, as a function
// because we want to control reactivity. When you check a todo item, the
// `list.incompleteCount` changes. If we didn't do this the entire list would
// re-render whenever you checked an item. By isolating the reactiviy on the list

// to the area that cares about it, we stop it from happening.


list() {
return Lists.findOne(listId);
},
// By finding the list with only the `_id` field set, we don't create a dependen
cy on the
// `list.incompleteCount`, and avoid re-rendering the todos when it changes
todos: Lists.findOne(listId, {fields: {_id: true}}).todos()
};
}
});

It's the listShow component (a reusuable component) that actually handles the job of
rendering the content of the page. As the page component is passing the arguments into the
reusuable component, it is able to be quite mechanical and the concerns of talking to the
router and rendering the page have been separated.

Changing page when logged out


There are types of rendering logic that appear related to the route but which also seem
related to user interface rendering. A classic example is authorization; for instance, you may
want to render a login form for some subset of your pages if the user is not yet logged in.

It's best to keep all logic around what to render in the component hierarchy (i.e. the tree of
rendered components). So this authorization should happen inside a component. Suppose
we wanted to add this to the Lists_show_page we were looking at above. We could do
something like:

146
URLs and Routing

<template name="Lists_show_page">
{{#if currentUser}}
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
{{else}}
Please log in to edit posts.
{{/if}}
</template>

Of course, we might find that we need to share this functionality between multiple pages of
our app that require access control. We can easily share functionality between templates by
wrapping them in a wrapper "layout" component which includes the behavior we want.

You can create wrapper components by using the "template as block helper" ability of Blaze
(see the Blaze Article). Here's how we could write an authorization template:

<template name="App_forceLoggedIn">
{{#if currentUser}}
{{> Template.contentBlock}}
{{else}}
Please log in see this page.
{{/if}}
</template>

Once that template exists, we can simply wrap our Lists_show_page :

<template name="Lists_show_page">
{{#App_forceLoggedIn}}
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
{{/App_forceLoggedIn}}
</template>

The main advantage of this approach is that it is immediately clear when viewing the
Lists_show_page what behavior will occur when a user visits the page.

Multiple behaviors of this type can be composed by wrapping a template in multiple


wrappers, or creating a meta-wrapper that combines multiple wrapper templates.

147
URLs and Routing

Changing Routes
Rendering an updated UI when a user reaches a new route is not that useful without giving
the user some way to reach a new route! The simplest way is with the trusty <a> tag and a
URL. You can generate the URLs yourself using FlowRouter.pathFor , but it is more
convenient to use the arillo:flow-router-helpers package that defines some helpers for
you:

meteor add arillo:flow-router-helpers

Now that you have this package, you can use helpers in your templates to display a link to a
certain route. For example, in the Todos example app, our nav links look like:

<a href="{{pathFor 'Lists.show' _id=list._id}}" title="{{list.name}}"


class="list-todo {{activeListClass list}}">

Routing programmatically
In some cases you want to change routes based on user action outside of them clicking on a
link. For instance, in the example app, when a user creates a new list, we want to route them
to the list they just created. We do this by calling FlowRouter.go() once we know the id of
the new list:

import { insert } from '../../api/lists/methods.js';

Template.App_body.events({
'click .js-new-list'() {
const listId = insert.call();
FlowRouter.go('Lists.show', { _id: listId });
}
});

You can also change only part of the URL if you want to, using the FlowRouter.setParams()
and FlowRouter.setQueryParams() . For instance, if we were viewing one list and wanted to
go to another, we could write:

FlowRouter.setParams({_id: newList._id});

Of course, calling FlowRouter.go() , will always work, so unless you are trying to optimize
for a specific situation it's better to use that.

148
URLs and Routing

Storing data in the URL


As we discussed in the introduction, the URL is really just a serialization of some part of the
client-side state the user is looking at. Although parameters can only be strings, it's possible
to convert any type of data to a string by serializing it.

In general if you want to store arbitrary serializable data in a URL param, you can use
EJSON.stringify() to turn it into a string. You'll need to URL-encode the string using

encodeURIComponent to remove any characters that have meaning in a URL:

FlowRouter.setQueryParams({data: encodeURIComponent(EJSON.stringify(data))});

You can then get the data back out of Flow Router using EJSON.parse() . Note that Flow
Router does the URL decoding for you automatically:

const data = EJSON.parse(FlowRouter.getQueryParam('data'));

Redirecting
Sometimes, your users will end up on a page that isn't a good place for them to be. Maybe
the data they were looking for has moved, maybe they were on an admin panel page and
logged out, or maybe they just created a new object and you want them to end up on the
page for the thing they just created.

Usually, we can redirect in response to a user's action by calling FlowRouter.go() and


friends, like in our list creation example above, but if a user browses directly to a URL that
doesn't exist, it's useful to know how to redirect immediately.

If a URL is simply out-of-date (sometimes you might change the URL scheme of an
application), you can redirect inside the action function of the route:

FlowRouter.route('/old-list-route/:_id', {
action(params) {
FlowRouter.go('Lists.show', params);
}
});

Redirecting dynamically

149
URLs and Routing

The above approach will only work for static redirects. However, sometimes you need to
load some data to figure out where to redirect to. In this case you'll need to render part of the
component hierarchy to subscribe to the data you need. For example, in the Todos example
app, we want to make the root ( / ) route redirect to the first known list. To achieve this, we
need to render a special App_rootRedirector route:

FlowRouter.route('/', {
name: 'App.home',
action() {
BlazeLayout.render('App_body', {main: 'App_rootRedirector'});
}
});

The App_rootRedirector component is rendered inside the App_body layout, which takes
care of subscribing to the set of lists the user knows about before rendering its sub-
component, and we are guaranteed there is at least one such list. This means that if the
App_rootRedirector ends up being created, there'll be a list loaded, so we can simply do:

Template.App_rootRedirector.onCreated(function rootRedirectorOnCreated() {
// We need to set a timeout here so that we don't redirect from inside a redirection
// which is a limitation of the current version of FR.
Meteor.setTimeout(() => {
FlowRouter.go('Lists.show', Lists.findOne());
});
});

If you need to wait on specific data that you aren't already subscribed to at creation time,
you can use an autorun and subscriptionsReady() to wait on that subscription:

Template.App_rootRedirector.onCreated(function rootRedirectorOnCreated() {
// If we needed to open this subscription here
this.subscribe('lists.public');

// Now we need to wait for the above subscription. We'll need the template to
// render some kind of loading state while we wait, too.
this.autorun(() => {
if (this.subscriptionsReady()) {
FlowRouter.go('Lists.show', Lists.findOne());
}
});
});

Redirecting after a user's action

150
URLs and Routing

Often, you just want to go to a new route programmatically when a user has completed a
certain action. Above we saw a case (creating a new list) when we wanted to do it
optimistically---i.e. before we hear back from the server that the Method succeeded. We can
do this because we reasonably expect that the Method will succeed in almost all cases (see
the UI/UX article for further discussion of this).

However, if we wanted to wait for the method to return from the server, we can put the
redirection in the callback of the method:

Template.App_body.events({
'click .js-new-list'() {
lists.insert.call((err, listId) => {
if (!err) {
FlowRouter.go('Lists.show', { _id: listId });
}
});
}
});

You will also want to show some kind of indication that the method is working in between
their click of the button and the redirect completing. Don't forget to provide feedback if the
method is returning an error.

Advanced Routing
Missing pages
If a user types an incorrect URL, chances are you want to show them some kind of amusing
not-found page. There are actually two categories of not-found pages. The first is when the
URL typed in doesn't match any of your route definitions. You can use FlowRouter.notFound
to handle this:

// the App_notFound template is used for unknown routes and missing lists
FlowRouter.notFound = {
action() {
BlazeLayout.render('App_body', {main: 'App_notFound'});
}
};

The second is when the URL is valid, but doesn't actually match any data. In this case, the
URL matches a route, but once the route has successfully subscribed, it discovers there is
no data. It usually makes sense in this case for the page component (which subscribes and
fetches the data) to render a not-found template instead of the usual template for the page:

151
URLs and Routing

<template name="Lists_show_page">
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
<template>

Analytics
It's common to want to know which pages of your app are most commonly visited, and
where users are coming from. You can read about how to set up Flow Router based
analytics in the Deployment Guide.

Server Side Routing


As we've discussed, Meteor is a framework for client rendered applications, but this doesn't
always remove the requirement for server rendered routes. There are two main use cases
for server-side routing.

Server Routing for API access


Although Meteor allows you to write low-level connect handlers to create any kind of API you
like on the server-side, if all you want to do is create a RESTful version of your Methods and
Publications, you can often use the simple:rest package to do this easily. See the Data
Loading and Methods articles for more information.

If you need more control, you can use the comprehensive nimble:restivus package to
create more or less whatever you need in whatever ontology you require.

Server Rendering
The Blaze UI library does not have support for server-side rendering, so it's not possible to
render your pages on the server if you use Blaze. However, the React UI library does. This
means it is possible to render HTML on the server if you use React as your rendering
framework.

Although Flow Router can be used to render React components more or less as we've
described above for Blaze, at the time of this writing Flow Router's support for SSR is still
experimental. However, it's probably the best approach right now if you want to use SSR for
Meteor.

152
URLs and Routing

153
User Interfaces

After reading this guide, you'll know:

1. How to build reusable client side components in any user interface framework.
2. How to build a style guide to allow you to visually test such reusable components.
3. Patterns for building front end components in a performant way in Meteor.
4. How to build user interfaces in a maintainable and extensible way.
5. How to build components that can cope with a variety of different data sources.
6. How to use animation to keep users informed of changes.

View layers
Meteor officially supports three user interface (UI) rendering libraries, Blaze, React and
Angular. Blaze was created as part of Meteor when it launched in 2011, React was created
by Facebook in 2013, and Angular was created by Google in 2010. All three have been used
successfully by large production apps. Blaze is the easiest to learn and has the most full-
stack Meteor packages, but React and Angular are more developed and have larger
communities.

Syntax
Blaze uses an easy-to-learn Handlebars-like template syntax, with logic like {{#if}}
and {{#each}} interspersed in your HTML files. Template functions and CSS-selector
events maps are written in JavaScript files.
React uses JSX, with which you write your HTML in JavaScript. While it doesn't have
the logic-view separation most libraries have, it also has the most flexibility. Template
functions and event handlers are defined in the same file as the HTML part of the
component, which usually makes it easier to understand how they are tied together.
Angular uses HTML with special attribute syntax for logic and events. Template helpers
are written in the accompanying JavaScript file along with events, which are called by
name from inside HTML attributes.
React and Angular enforce a better component structure, which makes developing
larger apps easier. (Although you can add component structure to Blaze by following
conventions or using the Blaze Components or ViewModel packages.)

Community
Blaze has many full-stack Meteor packages on Atmosphere, such as
useraccounts:core and aldeed:autoform .

React has 42k stars on Github and 13k npm libraries.


Angular has 12k stars on Github and 4k npm libraries.

154
User Interfaces

Performance
Render performance varies a lot depending on the situation. All three libraries are very
quick at rendering simple apps, but can take a noticeable amount of time with more
complex apps.
Angular and React have had more performance optimization work put into them than
Blaze and in general will perform better. However, there are some cases when Blaze
does better (for instance an {{#each}} over a changing cursor).
One test benchmarks Angular 2 as the best, followed by React and Angular 1, followed
by Blaze.

Mobile
Cordova
All three libraries work fine in a Cordova web view, and you can use mobile CSS
libraries like Ionic's CSS with any view library.
The most advanced mobile web framework is Ionic 2, which uses Angular 2.
Ionic 1 uses Angular 1, but there are also Blaze and React ports.
Another good option is Onsen UI, which includes a React version.
Native
You can connect any native iOS or Android app to a Meteor server via DDP. For
iOS, use the meteor-ios framework.
You can write apps with native UI elements in JavaScript using React Native. For
the most recent information on how to use React Native with Meteor, see this
reference.

UI components
Regardless of the view layer that you are using, there are some patterns in how you build
your User Interface (UI) that will help make your app's code easier to understand, test, and
maintain. These patterns, much like general patterns of modularity, revolve around making
the interfaces to your UI elements very clear and avoiding using techniques that bypass
these known interfaces.

In this article, we'll refer to the elements in your user interface as "components". Although in
some systems, you may refer to them as "templates", it can be a good idea to think of them
as something more like a component, which has an API and internal logic, rather than a
template, which is just a bit of HTML.

To begin with, let's consider two categories of UI components that are useful to think about,
"reusable" and "smart":

155
User Interfaces

Reusable components
A "reusable" component is a component which doesn't rely on anything from the
environment it renders in. It renders purely based on its direct inputs (its template arguments
in Blaze, or props in React) and internal state.

In Meteor specifically, this means a component which does not access data from any global
sources---Collections, Stores, routers, user data, or similar. For instance, in the Todos
example app, the Lists_show template takes in the list it is rendering and the set of todos
for that list, and it never looks directly in the Todos or Lists collections.

Reusable components have many advantages:

1. They are easy to reason about---you don't need to understand how the data in the
global store changes, simply how the arguments to the component change.

2. They are easy to test---you don't need to be careful about the environment you render
them in, all you need to do is provide the right arguments.

3. They are easy to add to component style guides---as we'll see in the section about
component style guides, when creating a style guide, a clean environment makes things
much easier to work with.

4. You know exactly what dependencies you need to provide for them to work in different
environments.

There's also an even more restricted type of reusable component, a "pure" component,
which does not have any internal state. For instance in the Todos app, the Todos_item
template decides what to render solely based on its arguments. Pure components are
even easier to reason about and test than reusable ones and so should be preferred
wherever possible.

Global data stores


So which are the global data stores that you should be avoiding in reusable components?
There are a few. Meteor is built to optimize speed of development, which means you can
access a lot of things globally. Although this is convenient when building "smart"
components (see below), you'll need to avoid these data sources in reusable components:

Your collections, as well as the Meteor.users collection,


Accounts information, like Meteor.user() and Meteor.loggingIn()
Current route information
Any other client-side data stores (read more in the Data Loading article)

156
User Interfaces

Smart components
While most of the components in your app should be reusable, they need to get their data
passed in from somewhere. This is where "smart" components come in. Such components
typically do the following things:

1. Subscribe to data
2. Fetch data from those subscriptions
3. Fetch global client-side state from stores such as the Router, Accounts, and your own
stores

Ideally, once a smart component has assembled such a set of data, it passes it off to a
reusable component child to render with. Smart components usually don't render anything
apart from one or more reusable children. This makes it easy to separate rendering and data
loading in your tests.

A typical use case for a smart component is the "page" component that the router points you
to when you access a URL. Such a component typically needs to do the three things above
and then can pass the resulting arguments into child components. In the Todos example
app, the listShowPage does exactly this, resulting in a template with very simple HTML:

<template name="Lists_show_page">
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
</template>

The JavaScript of this component is responsible for subscribing and fetching the data that's
used by the Lists_show template itself:

157
User Interfaces

Template.Lists_show_page.onCreated(function() {
this.getListId = () => FlowRouter.getParam('_id');

this.autorun(() => {
this.subscribe('todos.inList', this.getListId());
});
});

Template.Lists_show_page.helpers({
// We use #each on an array of one item so that the "list" template is
// removed and a new copy is added when changing lists, which is
// important for animation purposes.
listIdArray() {
const instance = Template.instance();
const listId = instance.getListId();
return Lists.findOne(listId) ? [listId] : [];
},
listArgs(listId) {
const instance = Template.instance();
return {
todosReady: instance.subscriptionsReady(),
// We pass `list` (which contains the full list, with all fields, as a function
// because we want to control reactivity. When you check a todo item, the
// `list.incompleteCount` changes. If we didn't do this the entire list would
// re-render whenever you checked an item. By isolating the reactiviy on the list

// to the area that cares about it, we stop it from happening.


list() {
return Lists.findOne(listId);
},
// By finding the list with only the `_id` field set, we don't create a dependen
cy on the
// `list.incompleteCount`, and avoid re-rendering the todos when it changes
todos: Lists.findOne(listId, {fields: {_id: true}}).todos()
};
}
});

Visually testing reusable components


A useful property of reusable components is that you can render them anywhere because
they don't rely on complicated environments. This is very useful when paired with
component explorers, debug-only apps that allow you to explore, visualize, and test your UI
components.

158
User Interfaces

A component explorer does two things:

1. Indexes your apps components so they are easy to find


2. Renders components using developer-defined states and stubbed data

For instance, in Galaxy, we use a component explorer called Chromatic to render each
component one specification at a time or with all specifications at once.

Chromatic component explorer walkthrough

Playback isn't supported on this device.

0:00 / 1:54

Using Chromatic enables rapid development of complex components. Typically in a large


application, it can be quite difficult to achieve certain states of components purely by "using"
the application. For example, a component in Galaxy can enter a complex state if two
deploys of the same app happen simultaneously. With Chromatic we're able to define this
state at the component level and test it independently of the application logic.

You can use Chromatic component explorer in your Meteor + React app with meteor add
mdg:chromatic . Similar projects built in React are UI Harness by Phil Cockfield and React

Storybook by Arunoda Susiripala.

User interface patterns

159
User Interfaces

Here are some patterns that are useful to keep in mind when building the user interface of
your Meteor application.

Internationalization
Internationalization (i18n) is the process of generalizing the UI of your app in such a way that
it's easy to render all text in a different language. Meteor's package ecosystem includes
internationalization options tailored to your frontend framework of choice.

Places to translate
It's useful to consider the various places in the system that user-readable strings exist and
make sure that you are properly using the i18n system to generate those strings in each
case. We'll go over the implementation for each case in the sections about tap:i18n and
universe:i18n below.

1. HTML templates and components. This is the most obvious place---in the content of
UI components that the user sees.
2. Client JavaScript messages. Alerts or other messages that are generated on the
client side are shown to the user, and should also be translated.
3. Server JavaScript messages and emails. Messages or errors generated by the
server can often be user visible. An obvious place is emails and any other server
generated messages, such as mobile push notifications, but more subtle places are
return values and error messages on method calls. Errors should be sent over the wire
in a generic form and translated on the client.
4. Data in the database. A final place where you may want to translate is actual user-
generated data in your database. For example, if you are running a wiki, you might want
to have a mechanism for the wiki pages to be translated to different languages. How
you go about this will likely be unique to your application.

Using `tap:i18n` in JavaScript


In Meteor, the excellent tap:i18n package provides an API for building translations and
using them in your components and frontend code.

To use tap:i18n , first meteor add tap:i18n to add it to your app. Then we need to add a
translation JSON file for our default language ( en for English) -- we can put it at
i18n/en.i18n.json . Once we've done that we can import and use the TAPi18n.__()

function to get translations for strings or keys within our JavaScript code.

160
User Interfaces

For instance for errors in the Todos example app, we create an errors module that allows
us to easily alert a translated error for all of the errors that we can potentially throw from
methods:

import { TAPi18n } from 'meteor/tap:i18n';

export const displayError = (error) => {


if (error) {
// It would be better to not alert the error here but inform the user in some
// more subtle way
alert(TAPi18n.__(error.error));
}
};

The error.error field is the first argument to the Meteor.Error constructor, and we use it
to uniquely name and namespace all the errors we use in the application. We then define
the English text of those errors in i18n/en.i18n.json :

{
"lists": {
"makePrivate": {
"notLoggedIn": "Must be logged in to make private lists.",
"lastPublicList": "Cannot make the last public list private."
},
"makePublic": {
...
}
...
}

Using `tap:i18n` in Blaze


We can also easily use translations in Blaze templates. To do so, we can use the {% raw %}
{{_ }}{% endraw %} helper. In the Todos app we use the actual string that we want to output

in English as the i18n key, which means we don't need to provide an English translation,
although perhaps in a real app you might want to provide keys from the beginning.

For example in app-not-found.html :

161
User Interfaces

<template name="App_notFound">
<div class="page not-found">
<nav>
<div class="nav-group">
<a href="#" class="js-menu nav-item"><span class="icon-list-unordered"></span>
</a>
</div>
</nav>

<div class="content-scrollable">
<div class="wrapper-message">
<div class="title-message">{{_ 'Page not found'}}</div>
</div>
</div>
</div>
</template>

Changing language
To set and change the language that a user is seeing, you should call
TAPi18n.setLanguage(fn) , where fn is a (possibly reactive) function that returns the current

language. For instance you could write

// A store to use for the current language


export const CurrentLanguage = new ReactiveVar('en');

import CurrentLanguage from '../stores/current-language.js';


TAPi18n.setLanguage(() => {
CurrentLanguage.get();
});

Then somewhere in your UI you can CurrentLanguage.set('es') when a user chooses a


new language.

Using `universe:i18n` in React


For React-based apps, the universe:18n package presents an alternative solution to
tap:i18n . universe:i18n adopts similar conventions to tap:i18n , but also includes a

convenient drop-in React component and omits tap:i18n's dependencies on Meteor's


templating and jquery packages. universe:i18n was intended for Meteor React

applications using ES2015 modules, but it can be used without React or modules.

Using `universe:i18n` in JS

162
User Interfaces

To get started, run meteor add universe:i18n to add it to your app. Add an English ( en-US )
translation file in JSON format to your app with the name en-us.i18n.json . Translation files
can be identified by file name or with the {"_locale": "en-US"} JSON property. The YAML
file format is also supported.

If your app uses ES2015 modules included from client/main.js and server/main.js entry
points, import your JSON file(s) there. The i18n.__() function will now locate keys you
pass.

Borrowing from the tap:i18n example above, in universe:i18n our displayError function
now looks like this:

import i18n from 'meteor/universe:i18n';

export const displayError = (error) => {


if (error) {
alert(i18n.__(error.error));
}
};

To change the user's language, use i18n.setLocale('en-US') . universe:i18n allows


retrieval of additional translations by method as well as including JSON files with a client
bundle.

Using `universe:i18n` in React components


To add reactive i18n inline in your React components, simply use the
i18n.createComponent() function and pass it keys from your translation file. Here's an

example of a simple component wrapping i18n's translation component:

import React from 'react';


import i18n from 'meteor/universe:i18n';

const T = i18n.createComponent();

// displays 'hello world!' where our en-us.i18n.json


// has the key/value { "hello": "hello world!" }

const Welcome = (props) => <div>


<T>hello</T>
</div>;

export default Welcome;

See the documentation for universe:i18n for additional options and configuration.

163
User Interfaces

Event handling
A large part of your UI involves responding to events initated by the user, and there are
some steps that you should take to make sure your application performs well in the face of
rapid input. An application lagging in response to user actions is one of the most noticeable
performance problems.

Throttling method calls on user action


It's typical to make some kind of change to the database when a user takes an action.
However it's important to make sure you don't do this too rapidly. For instance, if you wish to
save the user's text as they type in a text box, you should take steps to make sure that you
don't send method calls to your server more than every few hundred milliseconds.

If you do not, you'll see performance problems across the board: you'll be flooding the user's
network connection with a lot of small changes, the UI will update on every keystroke,
potentially causing poor performance, and your database will suffer with a lot of writes.

To throttle writes, a typical approach is to use underscore's .throttle() or .debounce()


functions. For instance, in the Todos example app, we throttle writes on user input to 300ms:

import {
updateText,
} from '../../api/todos/methods.js';

Template.Todos_item.events({
// update the text of the item on keypress but throttle the event to ensure
// we don't flood the server with updates (handles the event at most once
// every 300ms)
'keyup input[type=text]': _.throttle(function(event) {
updateText.call({
todoId: this.todo._id,
newText: event.target.value
}, (err) => {
err && alert(err.error);
});
}, 300)
});

Typically, you use .throttle() if you are OK with the event happening during the user
series of actions (i.e. you don't mind the multiple, throttled events happening over time, as in
this case), whereas you use .debounce() if you want the events to happen whenever (in
this example) the user stops typing for 300ms or longer.

164
User Interfaces

Limiting re-rendering
Even if you aren't saving data over the wire to the database on every user input, sometimes
you still may wish to update in-memory data stores on every user change. If updating that
data store triggers a lot of UI changes, you can see poor performance and missed
keystrokes when you update it too often. In such cases you can limit re-rendering by
throttling in a similar way how we throttled the method call above. You could also use
.debounce() to ensure the changes happen only after the user has stopped typing.

User experience patterns


User experience, or UX, describes the experience of a user as they interact with your
application. There are several UX patterns that are typical to most Meteor apps which are
worth exploring here. Many of these patterns are tied to the way data is loaded as the user
interacts with your app, so there are similar sections in the data loading article talking about
how to implement these patterns using Meteor's publications and subscriptions.

Subscription readiness
When you subscribe to data in Meteor, it does not become instantly available on the client.
Typically the user will need to wait for a few hundred milliseconds, or as long as a few
seconds (depending on the connection speed), for the data to arrive. This is especially
noticeable when the app is first starting up or you move between screens that are displaying
completely new data.

There are a few UX techniques for dealing with this waiting period. The simplest is simply to
switch out the page you are rendering with a generic "loading" page while you wait for all the
data (typically a page may open several subscriptions) to load. As an example, in the Todos
example app, we wait until all the public lists and the user's private lists have loaded before
we try to render the actual page:

165
User Interfaces

{{#if Template.subscriptionsReady}}
{{> Template.dynamic template=main}}
{{else}}
{{> App_loading}}
{{/if}}

We do this with Blaze's Template.subscriptionsReady which is perfect for this purpose, as it


waits for all the subscriptions that the current component has asked for to become ready.

Per-component loading state


Usually it makes for a better UX to show as much of the screen as possible as quickly as
possible and to only show loading state for the parts of the screen that are still waiting on
data. So a nice pattern to follow is "per-component loading". We do this in the Todos app
when you visit the list page---we instantly render the list metadata, such as its title and
privacy settings, and render a loading state for the list of todos while we wait for them to
appear.

166
User Interfaces

We achieve this by passing the readiness of the todos list down from the smart component
which is subscribing (the listShowPage ) into the reusable component which renders the
data:

{{> Lists_show todosReady=Template.subscriptionsReady list=list}}

And then we use that state to determine what to render in the reusable component
( listShow ):

{{#if todosReady}}
{{#with list._id}}
{{#each todo in (todos this)}}
{{> Todos_item (todoArgs todo)}}
{{else}}
<div class="wrapper-message">
<div class="title-message">No tasks here</div>
<div class="subtitle-message">Add new tasks using the field above</div>
</div>
{{/each}}
{{/with}}
{{else}}
<div class="wrapper-message">
<div class="title-message">Loading tasks...</div>
</div>
{{/if}}

Showing placeholders
You can take the above UI a step further by showing placeholders whilst you wait for the
data to load. This is a UX pattern that has been pioneered by Facebook which gives the user
a more solid impression of what data is coming down the wire. It also prevents parts of the
UI from moving around when data loads, if you can make the placeholder have the same
dimensions as the final element.

For example, in Galaxy, while you wait for your app's log to load, you see a loading state
indicating what you might see:

167
User Interfaces

Using the style guide to prototype loading state


Loading states are notoriously difficult to work on visually as they are by definition transient
and often are barely noticeable in a development environment where subscriptions load
almost instantly.

This is one reason why being able to achieve any state at will in the component style guide
is so useful. As our reusable component Lists_show simply chooses to render based on its
todosReady argument and does not concern itself with a subscription, it is trivial to render its

loading state in a style guide.

Pagination
In the Data Loading article we discuss a pattern of paging through an "infinite scroll" type
subscription which loads one page of data at a time as a user scrolls down the page. It's
interesting to consider UX patterns to consume that data and indicate what's happening to
the user.

A list component

168
User Interfaces

Let's consider any generic item-listing component. To focus on a concrete example, we


could consider the todo list in the Todos example app. Although it does not in our current
example app, in a future version it could paginate through the todos for a given list.

There are a variety of states that such a list can be in:

1. Initially loading, no data available yet.


2. Showing a subset of the items with more available.
3. Showing a subset of the items with more loading.
4. Showing all the items - no more available.
5. Showing no items because none exist.

It's instructive to think about what arguments such a component would need to differentiate
between those five states. Let's consider a generic pattern that would work in all cases
where we provide the following information:

A count of the total number of items.


A countReady boolean that indicates if we know that count yet (remember we need to
load even that information).
A number of items that we have requested .
A list of items that we currently know about.

We can now distinguish between the 5 states above based on these conditions:

1. countReady is false, or count > 0 and items is still empty. (These are actually two

different states, but it doesn't seem important to visually separate them).


2. items.length === requested && requested < count

3. 0 < items.length < requested

4. items.length === requested && count > 0

5. count === 0

You can see that although the situation is a little complex, it's also completely determined by
the arguments and thus very much testable. A component style guide helps immeasurably in
seeing all these states easily! In Galaxy we have each state in our style guide for each of the
lists of our app and we can ensure all work as expected and appear correctly:

169
User Interfaces

A pagination "controller" pattern


A list is also a good opportunity to understand the benefits of the smart vs reusable
component split. We've seen above that correctly rendering and visualizing all the possible
states of a list is non-trivial and is made much easier by having a reusable list component
that takes all the required information in as arguments.

However, we still need to subscribe to the list of items and the count, and collect that data
somewhere. To do this, it's sensible to use a smart wrapper component (analogous to an
MVC "controller") whose job it is to subscribe and fetch the relevant data.

In the Todos example app, we already have a wrapping component for the list that talks to
the router and sets up subscriptions. This component could easily be extended to
understand pagination:

170
User Interfaces

const PAGE_SIZE = 10;

Template.Lists_show_page.onCreated(function() {
// We use internal state to store the number of items we've requested
this.state = new ReactiveDict();

this.getListId = () => FlowRouter.getParam('_id');

this.autorun(() => {
// As the `requested` state increases, we re-subscribe to a greater number of todos

this.subscribe('List.todos', this.getListId(), this.state.get('requested'));


this.countSub = this.subscribe('Lists.todoCount', this.getListId());
});

// The `onNextPage` function is used to increment the `requested` state variable. It


's passed
// into the listShow subcomponent to be triggered when the user reaches the end of t
he visible todos
this.onNextPage = () => {
this.state.set('requested', this.state.get('requested') + PAGE_SIZE);
};
});

Template.Lists_show_page.helpers({
listArgs(listId) {
const instance = Template.instance();
const list = Lists.findOne(listId);
const requested = instance.state.get('requested');
return {
list,
todos: list.todos({}, {limit: requested}),
requested,
countReady: instance.countSub.ready(),
count: Counts.get(`list/todoCount${listId}`),
onNextPage: instance.onNextPage
};
}
});

UX patterns for displaying new data


An interesting UX challenge in a realtime system like Meteor involves how to bring new
information (like changing data in a list) to the user's attention. As Dominic points out, it's not
always a good idea to simply update the contents of a list as quickly as possible, as it's easy
to miss changes or get confused about what's happened.

171
User Interfaces

One solution to this problem is to animate list changes (which we'll look at in the animation
section), but this isn't always the best approach. For instance, if a user is reading a list of
comments, they may not want to see any changes until they are done with the current
comment thread.

An option in this case is to call out that there are changes to the data the user is looking at
without actually making UI updates. In a system like Meteor which is reactive by default, it
isn't necessarily easy to stop such changes from happening!

However, it is possible to do this thanks to our split between smart and reusable
components. The reusable component simply renders what it's given, so we use our smart
component to control that information. We can use a local collection to store the rendered
data, and then push data into it when the user requests an update:

Template.Lists_show_page.onCreated(function() {
// ...

// The visible todos are the todos that the user can
// actually see on the screen (whereas Todos are the
// todos that actually exist)
this.visibleTodos = new Mongo.Collection(null);

this.getTodos = () => {
const list = Lists.findOne(this.getListId());
return list.todos({}, {limit: instance.state.get('requested')});
};
// When the user requests it, we should sync the visible todos to
// reflect the true state of the world
this.syncTodos = (todos) => {
todos.forEach(todo => this.visibleTodos.insert(todo));
this.state.set('hasChanges', false);
};
this.onShowChanges = () => {
this.syncTodos(this.getTodos());
};

this.autorun((computation) => {
const todos = this.getTodos();

// If this autorun re-runs, the list id or set of todos must have


// changed, so we should flag it to the user so they know there
// are changes to be seen.
if (!computation.firstRun) {
this.state.set('hasChanges', true);
} else {
this.syncTodos(todos);
}
});
});

172
User Interfaces

Template.Lists_show_page.helpers({
listArgs(listId) {
const instance = Template.instance();
const list = Lists.findOne(listId);
const requested = instance.state.get('requested');
return {
list,
// we pass the *visible* todos through here
todos: instance.visibleTodos.find({}, {limit: requested}),
requested,
countReady: instance.countSub.ready(),
count: Counts.get(`list/todoCount${listId}`),
onNextPage: instance.onNextPage,
// These two properties allow the user to know that there
// are changes to be viewed and allow them to view them
hasChanges: instance.state.get('hasChanges'),
onShowChanges:instance.onShowChanges
};
}
});

The reusable sub-component can then use the hasChanges argument to determine if it
should show some kind of callout to the user to indicate changes are available, and then use
the onShowChanges callback to trigger them to be shown.

Optimistic UI
One nice UX pattern which Meteor makes much easier than other frameworks is Optimistic
UI. Optimistic UI is the process of showing user-generated changes in the UI without waiting
for the server to acknowledge that the change has succeeded, resulting in a user experience
that seems faster than is physically possible, since you don't need to wait for any server
roundtrips. Since most user actions in a well-designed app will be successful, it makes
sense for almost all parts of an app to be optimistic in this way.

However, it's not always necessarily a good idea to be optimistic. Sometimes we may
actually want to wait for the server's response. For instance, when a user is logging in, you
have to wait for the server to check the password is correct before you can start allowing
them into the site.

So when should you wait for the server and when not? It basically comes down to how
optimistic you are; how likely it is that something will go wrong. In the case of a password,
you really can't tell on the client, so you need to be conservative. In other cases, you can be
pretty confident that the Method call will succeed, and so you can move on.

For instance, in the Todos example app, when creating a new list, the list creation will
basically always succeed, so we write:

173
User Interfaces

import { insert } from '../../api/lists/methods.js';

Template.App_body.events({
'click .js-new-list'() {
const listId = insert.call((err) => {
if (err) {
// At this point, we have already redirected to the new list page, but
// for some reason the list didn't get created. This should almost never
// happen, but it's good to handle it anyway.
FlowRouter.go('App.home');
alert('Could not create list.');
}
});

FlowRouter.go('Lists.show', { _id: listId });


}
});

We place the FlowRouter.go('Lists.show') outside of the callback of the Method call, so


that it runs right away. First we simulate the method (which creates a list locally in
Minimongo), then route to it. Eventually the server returns, usually creating the exact same
list (which the user will not even notice). In the unlikely event that the server call fails, we
show an error and redirect back to the homepage.

Note that the listId returned by the list method (which is the one generated by the client
stub) is guaranteed to be the same as the one generated on the server, due to the way that
Meteor generates IDs and ensures they are the same between client and server.

Indicating when a write is in progress


Sometimes the user may be interested in knowing when the update has hit the server. For
instance, in a chat application, it's a typical pattern to optimistically display the message in
the chat log, but indicate that it is "pending" until the server has acknowledged the write. We
can do this easily in Meteor by simply modifying the Method to act differently on the client:

174
User Interfaces

Messages.methods.insert = new ValidatedMethod({


name: 'Messages.methods.insert',
validate: new SimpleSchema({
text: {type: String}
}).validator(),
run(message) {
// In the simulation (on the client), we add an extra pending field.
// It will be removed when the server comes back with the "true" data.
if (this.isSimulation) {
message.pending = true;
}

Messages.insert(message);
}
})

Of course in this scenario, you also need to be prepared for the server to fail, and again,
indicate it to the user somehow.

Unexpected failures
We've seen examples above of failures which you don't really anticipate will happen. It's
difficult and inefficient to defend against every possible error, however unlikely. However,
there are some catch-all patterns that you can use for unexpected failures.

Thanks to Meteor's automatic handling of optimistic UI, if a method unexpectedly fails the
optimistic changes will roll back and the Minimongo database will end up in a consistent
state. If you are rendering directly from Minimongo, the user will see something that is
consistent, even if it's not what they anticipated of course. In some cases when you have
state you are keeping outside of Minimongo, you may need to make changes to it manually
to reflect this. You can see this in the example above where we had to update the router
manually after an operation failed.

However, it's terrible UX to simply jump the user to an unexpected state without explaining
what's happened. We used a alert() above, which is a pretty poor option, but gets the job
done. One better approach is to indicate changes via a "flash notification", which is a UI
element that's displayed "out-of-band", typically in the top right of the screen, given the user
some indication of what's happened. Here's an example of a flash notification in Galaxy, at
the top right of the page:

175
User Interfaces

Animation
Animation is the process of indicating changes in the UI smoothly over time rather than
instantly. Although animation is often seen as "window dressing" or purely aesthetic, in fact it
serves a very important purpose, highlighted by the example of the changing list above. In a
connected-client world where changes in the UI aren't always initiated by user action (i.e.
sometimes they happen as a result of the server pushing changes made by other users),
instant changes can result in a user experience where it's difficult to understand what is
happening.

Animating changes in visiblity


Probably the most fundamental type of UI change that requires animation is when items
appear or disappear. In Blaze, we can use the percolate:momentum package to plug a
standard set of animations from the velocity animation library into such state changes.

A good example of this is the editing state of the list from the Todos example app:

176
User Interfaces

{{#momentum plugin="fade"}}
{{#if instance.state.get 'editing'}}
<form class="js-edit-form list-edit-form">...</form>
{{else}}
<div class="nav-group">...</div>
{{/if}}
{{/momentum}}

Momentum works by overriding the way that child HTML elements appear and disappear. In
this case, when the list component goes into the editing state, the .nav-group
disappears, and the form appears. Momentum takes care of the job of making sure that
both items fade, making the change a lot clearer.

Animating changes to attributes


Another common type of animation is when an attribute of an element changes. For
instance, a button may change color when you click on it. These type of animations are most
easily achieved with CSS transitions. For example, we use a CSS transition for the hover
state of links in the Todos example app:

a {
transition: all 200ms ease-in;
color: @color-secondary;
cursor: pointer;
text-decoration: none;

&:hover { color: darken(@color-primary, 10%); }


&:active { color: @color-well; }
&:focus { outline:none; } //removes FF dotted outline
}

Animating page changes


Finally, it's common to animate when the user switches between routes of the application.
Especially on mobile, this adds a sense of navigation to the app via positioning pages
relative to each other. This can be done in a similar way to animating things appearing and
disappearing (after all one page is appearing and other is disappearing), but there are some
tricks that are worth being aware of.

Let's consider the case of the Todos example app. Here we do a similar thing to achieve
animation between pages, by using Momentum in the main layout template:

177
User Interfaces

{{#momentum plugin="fade"}}
{{#if Template.subscriptionsReady}}
{{> Template.dynamic template=main}}
{{else}}
{{> App_loading}}
{{/if}}
{{/momentum}}

This looks like it should just work, but there's one problem: Sometimes the rendering system
will prefer to simply change an existing component rather than switching it out and triggering
the animation system. For example in the Todos example app, when you navigate between
lists, by default Blaze will try to simply re-render the Lists_show component with a new
listId (a changed argument) rather than pull the old list out and put in a new one. This is

an optimization that is nice in principle, but that we want to avoid here for animation
purposes. More specifically, we want to make sure the animation only happens when the
listId changes and not on other reactive changes.

To do so in this case, we can use a little trick (that is specific to Blaze, although similar
techniques apply to other view layers) of using the fact that the {% raw %}{{#each}}{% endraw
%} helper diffs arrays of strings, and completely re-renders elements when they change.

<template name="Lists_show_page">
{{#each listId in listIdArray}}
{{> Lists_show (listArgs listId)}}
{{else}}
{{> App_notFound}}
{{/each}}
</template>

Template.Lists_show_page.helpers({
// We use #each on an array of one item so that the "list" template is
// removed and a new copy is added when changing lists, which is
// important for animation purposes.
listIdArray() {
const instance = Template.instance();
const listId = instance.getListId();
return Lists.findOne(listId) ? [listId] : [];
}
});

178
Blaze

This content has moved to the Blaze Community Site.

179
React

After reading this guide, you'll know:

1. What React is, and why you would consider using it with Meteor.
2. How to install React in your Meteor application, and how to use it correctly.
3. How to integrate React with Meteor's realtime data layer.
4. How to route in a React/Meteor application.

Introduction
React is a JavaScript library for building reactive user interfaces developed and distributed
by the Facebook team. React is one of the three rendering libraries supported by Meteor;
the alternatives are Blaze and Angular. Here's a comparison of all three.

React has a vibrant and growing ecosystem and is used widely in production in a variety of
combinations with different frameworks.

To learn more about using React in general and coming up to speed with the library, you
should check out the React documentation, especially the thinking in React post, which
explains the React philosophy well.

To get started with React in Meteor, you can follow along the React tutorial. To see an
example of a more complete Meteor application built with React, check out the react
branch of the Todos example application. Where applicable, code examples in this article will
reference that app.

Installing and using React


To install React in Meteor 1.3 you should simply add it as an npm dependency:

meteor npm install --save react react-dom

This will install react into your project and allow you to access it within your files with
import React from 'react' . Most React code is written in JSX, which you can use by

default in Meteor if you include the ecmascript package (which is installed in all Meteor
apps by default).

180
React

import React from 'react';

export default class HelloWorld extends React.Component {


render() {
return (
<h1>Hello World</h1>
);
}
}

You can render a component heirarchy to the DOM using the react-dom package:

import { Meteor } from 'meteor/meteor';


import React from 'react';
import { render } from 'react-dom';
import HelloWorld from './HelloWorld.js';

Meteor.startup(() => {
render(<HelloWorld />, document.getElementById('app'));
});

You need to include a <div id="app"></div> in your body's HTML somewhere of course.

Every new Meteor app includes Blaze, Meteor's default templating system, by default. If you
are not planning on using React and Blaze together, you can remove Blaze from your
project by running:

meteor remove blaze-html-templates


meteor add static-html

Using 3rd party packages


If you'd like to use a third party React component that has been published on npm, you can
meteor npm install --save them and import from within your app.

For example, to use the excellent Griddle React package for making tables, you could run

meteor npm install --save griddle-react

Then, like with any other npm package, you can import the component in your application:

181
React

import React from 'react';


import Griddle from 'griddle-react';

export default class MyGriddler extends React.Component {


render() {
return (<Griddle ..../>);
}
}

If you are looking to write an Atmosphere package that wraps such a component, you need
to take some further steps.

React Components in Blaze


If you'd like to use React within a larger app built with Blaze (which is a good strategy if
you'd like to incrementally migrate an app from Blaze to React), you can use the react-
template-helper component which renders a react component inside a Blaze template. First

run meteor add react-template-helper , then use the React helper in your template:

<template name="userDisplay">
<div>Hello, {{username}}</div>
<div>{{> React component=UserAvatar userId=_id}}</div>
</template>

You will need to pass in the component class with a helper:

import { Template } from 'meteor/templating';

import './userDisplay.html';
import UserAvatar from './UserAvatar.js';

Template.userDisplay.helpers({
UserAvatar() {
return UserAvatar;
}
})

The component argument is the React component to include, which should be passed in
with a helper.

Every other argument is passed as a prop to the component when it is rendered.

Note that there a few caveats:

182
React

React components must be the only thing in the wrapper element. Due to a limitation of
React (see facebook/react #1970, #2484), a React component must be rendered as the
only child of its parent node, meaning it cannot have any siblings.

This means a React component also can't be the only thing in a Blaze template,
because it's impossible to tell where the template will be used.

Passing callbacks to a React component


To pass a callback to a React component that you are including with this helper, simply
make a template helper that returns a function, and pass it in as a prop, like so:

Template.userDisplay.helpers({
onClick() {
const instance = Template.instance();

// Return a function from this helper, where the template instance is in


// a closure
return () => {
instance.hasBeenClicked.set(true)
}
}
});

To use it in Blaze:

<template name="userDisplay">
<div>
{{> React component=UserAvatar userId=_id onClick=onClick}}
</div>
</template>

Blaze Templates in React


Just like we can use React components in Blaze templates, we can also use Blaze
templates in React components. This is similarly useful for a gradual transition strategy; but
more importantly, it allows us to continue to use the multitude of Atmosphere packages built
for Blaze in our React projects, as well as core packages like accounts-ui .

One easy way to do this is with the gadicc:blaze-react-component package. First run meteor
add gadicc:blaze-react-component , then import and use it in your components as follows:

183
React

import React from 'react';


import Blaze from 'meteor/gadicc:blaze-react-component';

const App = () => (


<div>
<Blaze template="itemsList" items={items} />
</div>
);

The <Blaze template="itemsList" items={items} /> line is the same as if you had written {%
raw %}{{> itemsList items=items}}{% endraw %} inside of a Blaze template. For other options

and further information, see the package's project page.

Using Meteor's data system


React is a front-end rendering library and as such doesn't concern itself with how data gets
into and out of components. On the other hand, Meteor has strong opinions about data!
Meteor operates in terms of publications and methods, used to subscribe to and modify the
data in your application.

To integrate the two systems, we've developed a react-meteor-data package which allows
React components to respond to data changes via Meteor's Tracker reactivity system.

Using `createContainer`
Once you've run meteor add react-meteor-data , you'll be able to import the
createContainer function, which allows you to create a container component which

provides data to your presentational components.

Note that "container components" are analogous to the "smart components" and
"presentational components" to the "reusable components" in the pattern we document
in the UI/UX article, if you'd like to read more about how this philosophy relates to
Meteor.

For example, in the Todos example app, we have a ListPage component, which renders list
metadata and the tasks in the list. In order to do so, it needs to subscribe to the
todos.inList publication, check that subscription's readiness, then fetch the list of todos

from the Todos collection.

It also needs to be responsive to reactive changes in the state of those actions (for instance
if a todo changes due to the action of another user). All this data loading complexity is a
typical use-case for a container-presentational component split, and the createContainer()
function makes it simple to do this.

184
React

We simply define the ListPage component as a presentational component that expects its
data to be passed in using React props :

import React from 'react';

export default class ListPage extends React.Component {


...
}

ListPage.propTypes = {
list: React.PropTypes.object,
todos: React.PropTypes.array,
loading: React.PropTypes.bool,
listExists: React.PropTypes.bool,
};

Then we create a ListPageContainer container component which wraps it and provides a


data source:

import { Meteor } from 'meteor/meteor';


import { Lists } from '../../api/lists/lists.js';
import { createContainer } from 'meteor/react-meteor-data';
import ListPage from '../pages/ListPage.js';

export default ListPageContainer = createContainer(({ params }) => {


const { id } = params;
const todosHandle = Meteor.subscribe('todos.inList', id);
const loading = !todosHandle.ready();
const list = Lists.findOne(id);
const listExists = !loading && !!list;
return {
loading,
list,
listExists,
todos: listExists ? list.todos().fetch() : [],
};
}, ListPage);

It's a good habit to name your container exactly like the component that it wraps, with the
word “Container” tacked onto the end. This way, when you're attempting to track down
issues in your code, it makes it much easier to locate the appropriate files/classes.

The container component created by createContainer() will reactively rerender the


wrapped component in response to any changes to reactive data sources accessed from
inside the function provided to it.

185
React

Although this ListPageContainer container is intended to be instantiated by the React


Router (which passes in the props automatically), if we did ever want to create one
manually, we would need to pass in the props to the container component (which then get
passed into our data function above):

<ListPageContainer params={{id: '7'}}/>

Preventing re-renders
Sometimes changes in your data can trigger re-computations which you know won't affect
your UI. Although React is in general quite efficient in the face of unnecessary re-renders, if
you need to control re-rendering, the above pattern allows you to easily use React's
shouldComponentUpdate on the presentational component to avoid re-renders.

Routing
There are two main options for routing with Meteor and React. Either way, we recommend
consulting our Routing article for some general principles of routing in Meteor before writing
your app.

kadira:flow-router is a Meteor specific router that can be used both with React and

Blaze. It is documented in detail in the Routing article.

react-router is a React-specific router very popular in the React community. It can

also be used easily with Meteor.

Flow Router
Using Flow Router with React is very similar to using it with Blaze. The only difference is that
in your route actions, you should use the react-mounter package to mount components
with a layout. Once you meteor npm install --save react-mounter , you can do the following:

186
React

import React from 'react';


import { FlowRouter } from 'meteor/kadira:flow-router';
import { mount } from 'react-mounter';

import AppContainer from '../../ui/containers/AppContainer.js';


import ListPageContainer from '../../ui/containers/ListPageContainer.js';

FlowRouter.route('/lists/:_id', {
name: 'Lists.show',
action() {
mount(AppContainer, {
main: <ListPageContainer/>,
});
},
});

Note that react-mounter automatically mounts the layout component on a #react-root


node, which you can change by using the withOptions() function.

In the below example, your App component would receive a main prop with a instantiated
React Element to render:

const App = (props) => (


<div>
<section id="menu"><..></section>
{props.main}
</div>
);

export default AppContainer = createContainer(props => {


// props here will have `main`, passed from the router
// anything we return from this function will be *added* to it
return {
user: Meteor.user(),
};
}, App);

React Router
Using React Router is also straightforward. Once you meteor npm install --save react-
router , you can simply export a list of nested routes as you would in any other React Router

driven React application:

187
React

import React from 'react';


import { Router, Route, browserHistory } from 'react-router';

// route components
import AppContainer from '../../ui/containers/AppContainer.js';
import ListPageContainer from '../../ui/containers/ListPageContainer.js';
import AuthPageSignIn from '../../ui/pages/AuthPageSignIn.js';
import AuthPageJoin from '../../ui/pages/AuthPageJoin.js';
import NotFoundPage from '../../ui/pages/NotFoundPage.js';

export const renderRoutes = () => (


<Router history={browserHistory}>
<Route path="/" component={AppContainer}>
<Route path="lists/:id" component={ListPageContainer}/>
<Route path="signin" component={AuthPageSignIn}/>
<Route path="join" component={AuthPageJoin}/>
<Route path="*" component={NotFoundPage}/>
</Route>
</Router>
);

With React Router, you'll also need to explicity render the exported routes in a startup
function:

import { Meteor } from 'meteor/meteor';


import { render } from 'react-dom';
import { renderRoutes } from '../imports/startup/client/routes.js';

Meteor.startup(() => {
render(renderRoutes(), document.getElementById('app'));
});

When using React Router in Meteor, you can follow roughly the same principles as when
using Flow Router, but you should also consider the idioms outlined in React Router's own
documentation.

These include some notable differences like:

React Router encourages you to couple your URL design and layout hierarchy in the
route definition. Flow Router is more flexible, although it can involve much more
boilerplate as a result.
React Router embraces React-specific functionality like the use of context, although you
can also explicitly pass your FlowRouter instance around in context if you'd like (in fact
this is probably the best thing to do).

Meteor and React


188
React

Using React in Atmosphere Packages


If you are writing an Atmosphere package and want to depend on React or an npm package
that itself depends on React, you can't use Npm.depends() and Npm.require() , as this will
result in 2 copies of React being installed into the application (and besides Npm.require()
only works on the server).

Instead, you need to ask your users to install the correct npm packages in the application
itself. This will ensure that only one copy of React is shipped to the client and there are no
version conflicts.

In order to check that a user has installed the correct versions of npm packages, you can
use the tmeasday:check-npm-versions package to check dependency versions at runtime.

189
Angular

Angular is a frontend rendering library that is officially supported by Meteor. The best place
to read about how to use both Angular 1 and Angular 2 in Meteor is the Angular-Meteor site.

The two alteratives to Angular are Blaze and React. Here's a comparison of all three.

190
Atmosphere vs. npm

Building an application completely from scratch is a tall order. This is one of the main
reasons you might consider using Meteor in the first place - you can focus on writing the
code that is specific to your app, instead of reinventing wheels like user login and data
synchronization. To streamline your workflow even further, it makes sense to use community
packages from npm and Atmosphere. Many of these packages are recommended in the
guide, and you can find more in the online directories.

With the release of version 1.3, Meteor has full support for npm. In the future, there
will be a time when all packages will be migrated to npm, but currently there are
benefits to both systems.

When to use Atmosphere packages


Atmosphere packages are packages written specifically for Meteor and have several
advantages over npm when used with Meteor. In particular, Atmosphere packages can:

Depend on core Meteor packages, such as ddp and blaze


Explicitly include non-javascript files including CSS, Less, Sass, Stylus and static assets
Take advantage of Meteor's build system to be automatically transpiled from languages
like CoffeeScript
Have a well defined way to ship different code for client and server, enabling different
behavior in each context
Get direct access to Meteor's package namespacing and package global exports
without having to explicitly use ES2015 import
Enforce exact version dependencies between packages using Meteor's constraint
resolver
Include build plugins for Meteor's build system
Include pre-built binary code for different server architectures, such as Linux or
Windows

If your package depends on an Atmosphere package (which, in Meteor 1.3, includes the
Meteor core packages), or needs to take advantage of Meteor's build system, writing an
Atmosphere package might be the best option for now.

When to use npm packages


npm is a repository of general JavaScript packages. These packages were originally
intended solely for the Node.js server-side environment, but as the JavaScript ecosystem
matured, solutions arose to enable the use of npm packages in other environments such as
the browser. Today, npm is used for all types of JavaScript packages.

191
Atmosphere vs. npm

If you want to distribute and reuse code that you've written for a Meteor application, then you
should consider publishing that code on npm if it's general enough to be consumed by a
wider JavaScript audience. It's simple to use npm packages in Meteor applications, and
possible to use npm packages within Atmosphere packages, so even if your main audience
is Meteor developers, npm might be the best choice.

Meteor comes with npm bundled so that you can type meteor npm without worrying
about installing it yourself. If you like, you can also use a globally installed npm to
manage your packages.

192
Using Atmosphere Packages

Searching for packages


There are a few ways to search for Meteor packages published to Atmosphere:

1. Search on the Atmosphere website.


2. Use meteor search from the command line.
3. Use a community package search website like Fastosphere.

The main Atmosphere website provides additional curation features like trending packages,
package stars, and flags, but some of the other options can be faster if you're trying to find a
specific package. For example, you can use meteor show kadira:flow-router from the
command line to see the description of that package and different available versions.

Package naming
You may notice that, with the exception of Meteor platform packages, all packages on
Atmosphere have a name of the form prefix:package-name . The prefix is the Meteor
Developer username of the organization or user that published the package. Meteor uses
such a convention for package naming to make sure that it's clear who has published a
certain package, and to avoid an ad-hoc namespacing convention. Meteor platform
packages do not have any prefix: .

Installing Atmosphere Packages


To install an Atmosphere package, you simply run meteor add :

meteor add kadira:flow-router

This will add the newest version of the desired package that is compatible with the other
packages in your app. If you want to specify a particular version, you can specify it by adding
a suffix to the package name like: meteor add kadira:flow-router@2.10.0 .

Regardless of how you add the package to your app, its actual version will be tracked in the
file at .meteor/versions . This means that anybody collaborating with you on the same app
is guaranteed to have the same package versions as you. If you want to update to a newer
version of a package after installing it, use meteor update . You can run meteor update
without any arguments to update all packages and Meteor itself to their latest versions, or
pass a specific package to update just that one, for example meteor update kadira:flow-
router .

193
Using Atmosphere Packages

If your app is running when you add a new package, Meteor will automatically download it
and restart your app for you.

The actual files for a given version of an Atmosphere package are stored in your local
~/.meteor/packages directory.

To see all the Atmosphere packages installed run:

meteor list

To remove an unwanted Atmosphere package run:

meteor remove kadira:flow-router

You can get more details on all the package commands in the Meteor Command line
documentation.

Using Atmosphere Packages


To use an Atmosphere Package in your app you can import it with the meteor/ prefix:

import { SimpleSchema } from 'meteor/aldeed:simple-schema';

Typically a package will export one or more symbols, which you'll need to reference with the
destructuring syntax. You can find these exported symbols by either looking in that
package's package.js file for api.export calls or by looking in that package's main
JavaScript file for ES2015 export calls like export const packageName = 'package-name'; .

Sometimes a package will have no exports and simply have side effects when included in
your app. In such cases you don't need to import the package at all after installing.

For backwards compatibility with Meteor 1.2 and early releases, Meteor by default
makes available directly to your app all symbols referenced in api.export in any
packages you have installed. However, it is recommended that you import these
symbols first before using them.

Importing styles from Atmosphere packages


Using any of Meteor's supported CSS pre-processors you can import other style files using
the {package-name} syntax as long as those files are designated to be lazily evaluated as
"import" files. To get more details on how to determine this see CSS source versus import

194
Using Atmosphere Packages

files.

@import '{prefix:package-name}/buttons/styles.import.less';

CSS files in an Atmosphere package are declared with api.addFiles , and therefore
will be eagerly evaluated by default, and then bundled with all the other CSS in your
app.

Peer npm dependencies


Atmosphere packages can ship with contained npm dependencies, in which case you don't
need to do anything to make them work. However, some Atmosphere packages will expect
that you have installed certain "peer" npm dependencies in your application.

Typically the package will warn you if you have not done so. For example, if you install the
react-meteor-data package into your app, you'll also need to install the react and the

react-addons-pure-render-mixin packages:

meteor npm install --save react react-addons-pure-render-mixin


meteor add react-meteor-data

Atmosphere package namespacing


Each Atmosphere package that you use in your app exists in its own separate namespace,
meaning that it sees only its own global variables and any variables provided by the
packages that it specifically uses. When a top-level variable is defined in a package, it is
either declared with local scope or package scope.

/**
* local scope - this variable is not visible outside of the block it is
* declared in and other packages and your app won't see it
*/
const alicePerson = {name: "alice"};

/**
* package scope - this variable is visible to every file inside of the
* package where it is declared and to your app
*/
bobPerson = {name: "bob"};

195
Using Atmosphere Packages

Notice that this is just the normal JavaScript syntax for declaring a variable that is local or
global. Meteor scans your source code for global variable assignments and generates a
wrapper that makes sure that your globals don't escape their appropriate namespace.

In addition to local scope and package scope, there are also package exports. A package
export is a "pseudo global" variable that a package makes available for you to use when you
install that package. For example, the email package exports the Email variable. If your
app uses the email package (and only if it uses the email package!) then your app can
access the Email symbol and you can call Email.send . Most packages have only one
export, but some packages might have two or three (for example, a package that provides
several classes that work together).

It is recommended that you use the ecmascript package and first call import { Email
} from 'meteor/email'; before calling Email.send in your app. It is also recommended

that package developers now use ES2015 export from their main JavaScript file
instead of api.export .

Your app sees only the exports of the packages that you use directly. If you use package A,
and package A uses package B, then you only see package A's exports. Package B's
exports don't "leak" into your namespace just because you used package A. Each app or
package only sees their own globals plus the APIs of the packages that they specifically use
and depend upon.

196
Using npm Packages

Searching for packages


The best way to find npm packages is by searching on npmjs.com. There are also some
websites that have special search features specifically for certain kinds of packages, like the
aptly named react-components.com.

npm on the client


Tools like browserify and webpack are designed to provide a Node-like environment on the
client so that many npm packages, even ones originally intended for the server, can run
unmodified. Meteor's ES2015 module system does this for you out of the box with no
additional configuration necessary. In most cases, you can simply import npm dependencies
from a client file, just as you would on the server.

When creating a new application Meteor installs the meteor-node-stubs npm package
to help provide this client browser compatibility. If you are upgrading an application to
Meteor 1.3 you may have to run meteor npm install --save meteor-node-stubs
manually.

Installing npm Packages


npm packages are configured in a package.json file at the root of your project. If you create
a new Meteor project, you will have such a file created for you. If not you can run meteor npm
init to create one.

To install a package into your app you run the npm install command with the --save flag:

meteor npm install --save moment

This will both update your package.json with information about the dependency and
download the package into your app's local node_modules/ directory. Typically, you don't
check the node_modules/ directory into source control and your teammates run meteor npm
install to get up to date when dependencies change:

meteor npm install

197
Using npm Packages

If the package is just a development dependency (i.e. it's used for testing, linting or the like)
then you should use --save-dev . That way if you have some kind of build script, it can do
npm install --production and avoid installing packages it doesn't need.

For more information about npm install , check out the official documentation.

Meteor comes with npm bundled so that you can type meteor npm without worrying
about installing it yourself. If you like, you can also use a globally installed npm to
manage your packages.

Using npm Packages


To use an npm package from a file in your application you simply import the name of the
package:

import moment from 'moment';

// this is equivalent to the standard node require:


const moment = require('moment');

This imports the default export from the package into the symbol moment .

You can also import specific functions from a package using the destructuring syntax:

import { isArray } from 'lodash';

You can also import other files or JS entry points from a package:

import { parse } from 'graphql/language';

Importing styles from npm


Using any of Meteor's supported CSS pre-processors you can import other style files
provided by an NPM into your application using both relative and absolute paths. However,
this will only work for the top-level app and will not work inside an Atmosphere package.

Importing styles from an npm package with an absolute path using the {} syntax, for
instance with Less:

@import '{}/node_modules/npm-package-name/button.less';

198
Using npm Packages

Importing styles from an npm package with a relative path:

@import '../../node_modules/npm-package-name/colors.less';

You can also import CSS directly from a JavaScript file to control load order if you have the
ecmascript package installed:

import 'npm-package-name/stylesheets/styles.css';

When importing CSS from a JavaScript file, that CSS is not bundled with the rest of the
CSS processed with the Meteor Build tool, but instead is put in your app's <head> tag
inside <style>...</style> after the main concatenated CSS file.

npm Shrinkwrap
package.json typically encodes a version range, and so each npm install command can

sometimes lead to a different result if new versions have been published in the meantime. In
order to ensure that you and the rest of your team are using the same exact same version of
each package, it's a good idea to use npm shrinkwrap after making any dependency
changes to package.json :

# after installing
meteor npm install --save moment
meteor npm shrinkwrap

This will create an npm-shrinkwrap.json file containing the exact versions of each
dependency, and you should check this file into source control. For even more precision (the
contents of a given version of a package can change), and to avoid a reliance on the npm
server during deployment, you should consider using npm shrinkpack .

Asyncronous Callbacks
Many npm packages rely on an asynchronous, callback or promise-based coding style. For
several reasons, Meteor is currently built around a synchronous-looking but still non-blocking
style using Fibers.

The global Meteor server context and every method and publication initialize a new fiber so
that they can run concurrently. Many Meteor APIs, for example collections, rely on running
inside a fiber. They also rely on an internal Meteor mechanism that tracks server

199
Using npm Packages

"environment" state, like the currently executing method. This means you need to initialize
your own fiber and environment to use asynchronous Node code inside a Meteor app. Let's
look at an example of some code that won't work, using the code example from the node-
github repository:

// Inside a Meteor method definition


updateGitHubFollowers() {
github.user.getFollowingFromUser({
user: 'stubailo'
}, (err, res) => {
// Using a collection here will throw an error
// because the asynchronous code is not in a fiber
Followers.insert(res);
});
}

Let's look at a few ways to resolve this issue.

`Meteor.bindEnvironment`
In most cases, simply wrapping the callback in Meteor.bindEnvironment will do the trick. This
function both wraps the callback in a fiber, and does some work to maintain Meteor's server-
side environment tracking. Here's the same code with Meteor.bindEnvironment :

// Inside a Meteor method definition


updateGitHubFollowers() {
github.user.getFollowingFromUser({
user: 'stubailo'
}, Meteor.bindEnvironment((err, res) => {
// Everything is good now
Followers.insert(res);
}));
}

However, this won't work in all cases - since the code runs asynchronously, we can't use
anything we got from an API in the method return value. We need a different approach that
will convert the async API to a synchronous-looking one that will allow us to return a value.

`Meteor.wrapAsync`
Many npm packages adopt the convention of taking a callback that accepts (err, res)
arguments. If your asynchronous function fits this description, like the one above, you can
use Meteor.wrapAsync to convert to a fiberized API that uses return values and exceptions
instead of callbacks, like so:

200
Using npm Packages

// Setup sync API


const getFollowingFromUserFiber =
Meteor.wrapAsync(github.user.getFollowingFromUser, github.user);

// Inside a Meteor method definition


updateGitHubFollowers() {
const res = getFollowingFromUserFiber({
user: 'stubailo'
});

Followers.insert(res);

// Return how many followers we have


return res.length;
}

If you wanted to refactor this and create a completely fiber-wrapper GitHub client, you could
write some logic to loop over all of the methods available and call Meteor.wrapAsync on
them, creating a new object with the same shape but with a more Meteor-compatible API.

Promises
Recently, a lot of npm packages have been moving to Promises instead of callbacks for their
API. This means you actually get a return value from the asynchronous function, but it's just
an empty shell where the real value is filled in later.

The good news is that Promises can be used with the new ES2015 async/await syntax
(available in the ecmascript package since Meteor 1.3) in a natural and synchronous-
looking style on both the client and the server.

If you declare your function async (which ends up meaning it returns a Promise itself), then
you can use the await keyword to wait on other promise inside. This makes it very easy to
serially call Promise-based libraries:

async function sendTextMessage(user) {


const toNumber = await phoneLookup.findFromEmail(user.emails[0].address);
return await client.sendMessage({
to: toNumber,
from: '+14506667788',
body: 'Hello world!'
});
}

Shrinkpack

201
Using npm Packages

Shrinkpack is a tool that gives you more bulletproof and repeatable builds than you get by
using npm shrinkwrap alone.

Essentially it copies a tarball of the contents of each of your npm dependencies into your
application source repository. This is essentially a more robust version of the npm-
shrinkwrap.json file that shrinkwrap creates, because it means your application's npm

dependencies can be assembled without the need or reliance on the npm servers being
available or reliable. This is good for repeatable builds especially when deploying.

To use shrinkpack, first globally install it:

npm install -g shrinkpack

Then use it directly after you shrinkwrap

meteor npm install moment


meteor npm shrinkwrap
shrinkpack

You should then check the generated node_shrinkwrap/ directory into source control, but
ensure it is ignored by your text editor.

NOTE: Although this is a good idea for projects with a lot of npm dependencies, it will not
affect Atmosphere dependencies, even if they themselves have direct npm dependencies.

202
Writing npm Packages

To create a new npm package:

mkdir my-package
cd my-package/
meteor npm init

The last command creates a package.json file and prompts you for the package
information. You may skip everything but name , version , and entry point . You can use
the default index.js for entry point . This file is where you set your package's exports:

// my-package/index.js
exports.myPackageLog = function() {
console.log("logged from my-package");
};

Now apps that include this package can do:

import { myPackageLog } from 'my-package'

myPackageLog(); // > "logged from my-package"

When choosing a name for your npm package, be sure to follow the npm guidelines.

Including in your app


When you are developing a new npm package for your app, there are a couple methods for
including the package in your app:

Inside node_modules: Place the package in your app's node_modules/ directory, and
add the package to source control. Do this when you want everything in a single
repository.

cd my-app/node_modules/
mkdir my-package
cd my-package/
meteor npm init
git add -f ./ # or use a git submodule

npm link: Place the package outside your app's directory in a separate repository and
use npm link . Do this when you want to use the package in multiple apps.

203
Writing npm Packages

cd ~/
mkdir my-package
cd my-package/
meteor npm init
cd ~/my-app/
meteor npm link ~/my-package

Other developers will also need to run the npm link command.

After either method, edit the dependencies attribute of my-app/package.json , adding "my-
package": "1.0.0" (use the same version number you chose during meteor npm init ).

Publishing your package


You can share your package with others by publishing it to the npm registry. While most
packages are public, you can control who may view and use your package with private
modules).

To publish publicly, follow these instructions. When you're done, anyone can add your
package to their app with npm install --save your-package .

If you want to share packages during development, we recommend using the above
methods instead of the registry. If you use the registry, then every time you change the
package, you need to increment the version number, publish, and then npm update my-
package inside your app.

Overriding packages with a local version


If you need to modify a package to do something that the published version doesn't do, you
can edit a local version of the package on your computer.

Let's say you want to modify the left-pad npm package. If you haven't already, run inside
your app directory:

meteor npm install --save left-pad

Now left-pad is included in your package.json , and the code has been downloaded to
node_modules/left_pad/ . Add the new directory to source control with:

git add -f node_modules/left_pad/

204
Writing npm Packages

Now you can edit the package, commit, and push, and your teammates will get your version
of the package. To ensure that your package doesn't get overwritten during an npm update ,
change the default caret version range in your package.json to an exact version.

Before:

"left-pad": "^1.0.2",

After:

"left-pad": "1.0.2",

An alternative method is maintaining a separate repository for the package and changing the
package.json version number to a git URL or tarball, but every time you edit the separate

repo, you'll need to commit, push, and npm update left-pad .

205
Mobile

After reading this guide, you'll know:

1. What Cordova is, and how Meteor integrates with it to build mobile apps from a single
codebase
2. How to set up your local machine for mobile development
3. How to run and debug your app on a mobile device or simulator/emulator
4. How hot code push allows you to update your mobile app's code without reinstalling the
app on your device or submitting a new version to the store
5. How to use Cordova plugins to take advantage of native device features
6. How to access local files and remote resources from your app
7. What you can do to create a good mobile user experience for your app
8. How to configure your app to use your own app icon, launch screen, and set other
preferences
9. How to build your project and submit your mobile app to the store

Meteor Cordova integration


Meteor integrates with Cordova, a well-known Apache open source project, to build mobile
apps from the same codebase you use to create regular web apps. With the Cordova
integration in Meteor, you can take your existing app and run it on an iOS or Android device
with a few simple commands.

A Cordova app is a web app written using HTML, CSS, and JavaScript as usual, but it runs
in a web view embedded in a native app instead of in a stand-alone mobile browser. An
important benefit of packaging up your web app as a Cordova app is that all your assets are
bundled with the app. This ensures your app will load faster than a web app running on a
remote server could, which can make a huge difference for users on slow mobile
connections. Another feature of the Cordova integration in Meteor is support for hot code
push, which allows you to update your app on users' devices without going through the
usual app store review process.

Cordova also opens up access to certain native device features through a plugin
architecture. Plugins allow you to use features not usually available to web apps, such as
accessing the device camera or the local file system, interact with barcode or NFC readers,
etc.

Because a Cordova app is a web app, this means you use standard web elements to create
your user interface instead of relying on platform-specific native UI components. Creating a
good mobile user experience is an art in itself, but is fortunately helped by the availability of
various frameworks and libraries.

206
Mobile

What about PhoneGap?


You may have heard of PhoneGap, and wonder how it relates to Cordova. PhoneGap is
a product name used by Adobe since 2011, when they acquired a company called
Nitobi, the original creators of what is now the Cordova project. When Adobe donated
the code to Apache in 2012 to ensure a more open governance model, the open source
project was rebranded as Cordova. PhoneGap is now one of the distributions of
Cordova, on a par with other distributions like Ionic, Telerik, Monaca, or Intel XDK.
These distributions mainly differ in tooling and integration with cloud services, but they
share the underlying platform and plugins. Meteor could also be considered a Cordova
distribution.

How does it work?


With Meteor, there is no need to install Cordova yourself, or use the cordova command
directly. Cordova project creation happens as part of the Meteor run and build commands,
and the project itself is considered a build artifact (stored in .meteor/local/cordova-build in
your app directory) that can be deleted and recreated at any time. Instead of having you
modify Cordova's config.xml file, Meteor reads a mobile-config.js file in the root of your
app directory and uses the settings specified there to configure the generated project.

Cordova apps don’t load web content over the network, but rely on locally stored HTML,
CSS, JavaScript code and other assets. While Cordova by default uses file:// URLs to
load the app, Meteor includes an integrated file serving mechanism on the device to support
both bundling the initial assets and incrementally updating your app through hot code push.
This means your app will be served from http://localhost:<port> , which also has the
benefit that web views consider it a secure origin and won't block any sensitive features
(which they increasingly do for file:// URLs).

207
Mobile

What port will your app be served from?


While Meteor uses a built-in request interception mechanism on Android, supporting
WKWebView on iOS requires running a real embedded web server instead. That means

the local web server needs a port to bind to, and we can’t simply use a fixed port
because that might lead to conflicts when running multiple Meteor Cordova apps on the
same device. The easiest solution may seem to use a randomized port, but this has a
serious drawback: if the port changes each time you run the app, web features that
depend on the origin (like caching, localStorage, IndexedDB) won’t persist between
runs, and you also wouldn't be able to specify a stable OAuth redirect URL. So instead
we now pick a port from a predetermined range (12000-13000), calculated based on
the appId , a unique identifier that is part of every Meteor project. That ensures the
same app will always use the same port, but it hopefully avoids collisions betweens
apps as much as possible. (There is still a theoretical possibility of the selected port
being in use. Currently, starting the local server will fail in that case.)

The runtime environment


Cordova apps run in a web view. A web view is basically a browser without the browser UI.
Browser engines differ in their underlying implementation and in what web standards they
support. As a result, what web view your app runs on can have a huge impact on your app's
performance and on the features you get to use. (If you want to know what features are
supported on what browsers and versions, caniuse.com is a great resource.)

iOS
The browser on iOS is Safari, which is based on the open source WebKit project, but tends
to be somewhat slow in enabling new features. Because they use the same underlying
framework, the features available to a web view match the features supported by Safari on
the iOS release you're running on.

Meteor uses WKWebView by default, on both iOS 8 and iOS 9. WKWebView is part of the
modern WebKit API introduced in iOS 8, and replaces UIWebView, which has been in iOS
from the beginning. Its main benefit is that it runs in a separate process, allowing for much
higher JavaScript performance (3–4x in some benchmarks!) because it can take advantage
of Just-In-Time compilation (which UIWebView, running in the same process as your app,
cannot do for security reasons).

208
Mobile

You may be aware that WKWebView on iOS 8 doesn't allow files to be loaded from the
local filesystem. This is problematic for standard Cordova apps, because these use
file:// URLs to load the app. But because the Meteor integration serves assets from

localhost , WKWebView works fine on both iOS 8 and iOS 9.

Android
The web view situation on Android is a little more complicated. On older Android versions,
the included web view is known to be rather slow and buggy. That improved somewhat with
Android 4.4, which includes a web view based on Chromium, the open source project behind
the Chrome browser.

Android 5.0 also comes with a web view based on Chromium known as the Android System
Web View, but a big improvement is that it can be automatically updated through the Play
Store. This means updates to the web view happen more regularly and are independent of
OS updates.

This threatens to leave many older Android devices behind however, because they would be
stuck on the web view included with the OS and are often unable to update to newer OS
versions. Fortunately, the Crosswalk plugin allows you to embed Chromium in your app and
use it instead of the web view that comes with the OS on any of the Android versions
supported by Cordova (currently Android 4.0 or higher). Embedding Chromium means the
size of your APK will grow by about 20MB, but the benefit is that you can rely on a
consistent web view with considerably better performance and enhanced standards support.

You can add the Crosswalk plugin to your app with meteor add crosswalk .

If you receive an error message trying to run the app on your device after adding or
removing the Crosswalk plugin, you may have to remove the existing app from your
device first.

Adding Cordova platforms


Every Meteor project targets a set of platforms. Platforms can be added to a Meteor project
with meteor add-platform .

meteor add-platform ios adds the iOS platform to a project.

meteor add-platform android adds the Android platform to a project.

meteor remove-platform ios android will remove the iOS and Android platforms from a

project.
meteor list-platforms lists the platforms targeted by your project.

209
Mobile

If your local machine does not (yet) fulfill the prerequisites for building apps for a mobile
platform, an error message with a list of missing requirements is printed (but the platform is
still added). You will have to make sure these requirements are fulfilled before you're able to
build and run mobile apps from your machine.

Installing prerequisites
In order to build and run mobile apps, you will need to install some prerequisites on your
local machine.

iOS
In order to build and run iOS apps, you will need a Mac with Xcode 7.2 or higher installed.

Installing Xcode from the App Store


meteor add-platform ios will open a dialog asking you whether you want to install the

'command line developer tools'. Do not select 'Install' here, because a full Xcode installation
is required to build and run iOS apps. Instead, selecting 'Get Xcode' will open the Mac App
Store page for Xcode and you can click install there. (Alternatively, you can open the Mac
App Store and search for 'Xcode' to get to that same page.)

Accepting the license agreement


After the download and installation completes, you will need to accept the license
agreement. If you start Xcode for the first time, a dialog will pop up where you can read the
license agreement and accept it. You can close Xcode directly afterwards.

A shortcut is to run sudo xcodebuild -license accept from the command line. (You will still
be expected to have read and understood the Xcode and Apple SDKs Agreement).

Android
In order to build and run Android apps, you will need to:

Install a Java Development Kit (JDK)


Install the Android SDK and download the required tools, platforms, and other
components (which is done most easily by installing Android Studio)
Set ANDROID_HOME and add the tools directories to your PATH
Optionally: Create an Android Virtual Device to run apps on an emulator

210
Mobile

Installing the Java Development Kit (JDK)


On Linux, you may want to use your distribution's package manager to install a JDK; on
Ubuntu, you can even use Ubuntu Make to install Android Studio and all dependencies
at the same time.

1. Open the Oracle Java website, and select the Java Platform (JDK)
2. Check the box to accept the license agreement, and select the correct download for
your platform
3. After it has downloaded, launch the installer, and complete the installation steps

Installing Android Studio


The easiest way to get a working Android development environment is by installing Android
Studio, which offers a setup wizard on first launch that installs the Android SDK for you, and
downloads a default set of tools, platforms, and other components that you will need to start
developing.

Please refer to the Android Studio installation instructions for more details on the exact steps
to follow.

There is no need to use Android Studio if you prefer a stand-alone install. Just make
sure you install the most recent versions of the Android SDK Tools and download the
required additional packages yourself using the Android SDK Manager. Make sure to
select SDK Platform API 23, because that is what the version of Cordova we bundle
requires.

Using Ubuntu Make


If you're running Ubuntu, the easiest way to install both a Java Development Kit and Android
Studio is by using Ubuntu Make, a command line tool that sets up development
environments and dependencies for you.

If you're on Ubuntu 14.04 LTS, you'll have to add the Ubuntu Make ppa first:

sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make

sudo apt-get update

Then, you can install Ubuntu Make itself:

sudo apt-get install ubuntu-make

And finally you use Ubuntu Make to install Android Studio and all dependencies:

umake android

211
Mobile

Setting `ANDROID_HOME` and adding the tools directories


to your `PATH`
Cordova will detect an Android SDK installed in various standard locations automatically, but
in order to use tools like android or adb from the terminal, you will have to make some
changes to your environment.

Mac

Set the ANDROID_HOME environment variable to the location of the Android SDK. If you've
used the Android Studio setup wizard, it should be installed in ~/Library/Android/sdk
by default.
Add $ANDROID_HOME/tools , and $ANDROID_HOME/platform-tools to your PATH

You can do this by adding these lines to your ~/.bash_profile file (or the equivalent file for
your shell environment, like ~/.zshrc ):

# Android
export ANDROID_HOME="$HOME/Library/Android/sdk"
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools

You will then have to reload .bash_profile (by executing source ~/.bash_profile ) or open
a new terminal session to apply the new environment.

Optionally: Creating an Android Virtual Device (AVD) to run


apps on an emulator
The current Android emulator tends to be rather slow and can be unstable, so our
recommendation is to run your app on a physical device instead.

If you do want to run on an emulator however, you will have to create an Android Virtual
Device (AVD) using the AVD Manager. Make sure to configure one with API level 23,
because that is what the version of Cordova we bundle requires.

Developing on a device
During development, the Meteor build tool integrates with Cordova to run your app on a
physical device or the iOS Simulator/Android emulator. In addition to starting a development
server and MongoDB instance as usual, meteor run accepts arguments to run the app on
one or more mobile targets:

ios : Runs the app on the iOS Simulator

212
Mobile

Currently, this will always run your app on a simulated iPhone 6s Plus. Use ios-
device to open Xcode and select another simulator instead.

ios-device : Opens Xcode, where you can run the app on a connected iOS device or

simulator
android : Runs the app on the Android emulator

The current Android emulator tends to be rather slow and can be unstable. Our
recommendation is to run on a physical device or to use an alternative emulator
like Genymotion.

android-device : Runs the app on a connected Android device

You can specify multiple targets, so meteor run ios android-device will run the app on both
the iOS Simulator and an Android device for example.

Connecting to the server


A Meteor app should be able to connect to a server in order to load data and to enable hot
code push, which automatically updates a running app when you make changes to its files.
During development, this means the device and the computer you run meteor on will have
to be part of the same WiFi network, and the network configuration shouldn't prevent the
device from reaching the server. You may have to change your firewall or router settings to
allow for this (no client isolation).

meteor run will try to detect the local IP address of the computer running the command

automatically. If this fails, or if you would like your mobile app to connect to a different server,
you can specify an address using the --mobile-server option.

On iOS
Note: If you haven't previously developed iOS apps, or haven't used the connected
device for development, a series of dialogs and warnings may appear as Xcode
resolves code signing issues. It may also prompt you for permission to access the key
in your keychain. See Apple's instructions for more information.

1. Make sure the device is connected to your computer via a USB cable.
2. Connect the device to a WiFi network that allows for communication with the server.
3. Run meteor run ios-device to open your project in Xcode.
4. In the project navigator, choose your device from the Scheme toolbar menu:

213
Mobile

5. Click the Run button:


6. Xcode builds the app, installs it on the device, and launches it.

On Android
1. Make sure the device is connected to your computer via a USB cable.
2. Connect the device to a WiFi network that allows for communication with the server.
3. Make sure your device is set up for development as explained here.

214
Mobile

4. You may also need to click 'Allow' on the Allow USB debugging? prompt on the device.
5. Run meteor run android-device to build the app, install it on the device, and launch it.

To check if your device has been connected and set up correctly, you can run adb
devices to get a list of devices.

Logging and debugging


A full-stack mobile app consists of many moving parts, and this can make it difficult to
diagnose issues. Logging is indispensable in keeping track of what's going on in your app,
and may show warnings and errors that you would otherwise miss. Even more powerful is
remote debugging, which is the ability to interact with a mobile app running on a remote
device from a debugging interface in Safari (for iOS) or Chrome (for Android).

Different types of logs


You will encounter three types of logs in a Meteor Cordova app:

Server-side logs - Messages printed by the Meteor build system, and the result of
console logging calls from server-side code.

Client-side web logs - Warnings and errors from the web view, and the result of
console logging calls from client-side code.

Client-side native logs - Messages from system components and Cordova plugins.
This also includes more detailed logging from the Meteor plugin used for hot code push.

When using meteor run , server-side logs will be printed in the terminal as usual. In addition,
running on an Android device or emulator will print a subset of the logs to that same terminal
(these logs also include console logging calls made from client-side code).

Running on iOS will not show client-side logs in the terminal, but Xcode will show native logs
as usual in the debug console. You can add cordova-plugin-console to your project to output
console logging calls to the native logs (which Android does by default), but this isn't

recommended because it has a substantial performance impact, and remote debugging


gives you much nicer and more complete console output.

Although having client-side logs in the terminal can be useful, in most cases remote
debugging is a much better option. This allows you to use the debugging tools built into
Safari (for iOS apps) or Chrome (for Android apps) to investigate an app running on a
remote device or a simulator/emulator. Here, you can not only view the logs, but also interact
with running JavaScript code and the DOM, monitor network access, etc.

Debugging on iOS with Safari

215
Mobile

1. To use remote debugging in Safari, you'll first need to enable the Developer menu. Go
to Safari > Preferences and make sure 'Show Develop menu in menu bar' is checked:

216
Mobile

2. You'll also need to enable the Web Inspector on your iOS device. Go to Settings >
Safari > Advanced and enable 'Web Inspector':

3. Launch the app on your device and open remote debugger by choosing Develop >
<Your device> > <Your app>/localhost.

4. Because you can only connect to your app after it has started up, you sometimes miss
startup warnings and errors. You can invoke location.reload() in the Web Inspector
console to reload a running app, this time with the remote debugger connected.

You can find more information about remote debugging in the Safari Developer Guide.

217
Mobile

Debugging on Android with Chrome


See this article for instructions on how to remote debug your Android app with the Chrome
DevTools.

Because you can only connect to your app after it has started up, you sometimes miss
startup warnings and errors. You can invoke location.reload() in the DevTools
console to reload a running app, this time with the remote debugger connected.

Hot code push on mobile


During development, the Meteor build tool detects any relevant file changes, recompiles the
necessary files, and notifies all connected clients a new version is available. Clients can then
automatically reload the app, switching over to the new version of the code. This is referred
to as hot code push.

Meteor supports hot code push on both browser and mobile clients, but the process on
mobile is a bit different. In a browser, reloading the app will re-request assets from the
server, and the server will respond with the most recent versions. Because Cordova apps
rely on locally stored assets however, hot code push on mobile is a two step process:

1. Updated assets are downloaded from the server using native downloading
mechanisms, and stored on the device
2. The page is reloaded and the web view re-requests the assets from the local web
server

An important benefit of this is that while downloading may be slow over mobile connections,
this is done in the background, and we won't attempt to reload the app until all assets have
been downloaded to the device.

Downloading updates is done incrementally, so we only download assets that have actually
changed (based on a content hash). In addition, if we haven't been able to download all
changed assets in one go, because of a network failure or because the app was closed
before we finished, we will reuse the ones that have already completed downloading the
next time the app starts up or the network connection is restored.

In production
Hot code push greatly improves the development experience, but on mobile, it is also a
really useful feature for production apps, because it allows you to quickly push updates to
devices without having users update the app through the store and without going through a
possibly lengthy review process to get your update accepted.

218
Mobile

However, it is important to realize that hot code push can only be used to update the HTML,
CSS, JavaScript code and other assets making up your web app. Changes to native code
will still require you to submit a new version of your app to the store.

In order to avoid a situation where JavaScript code that relies on changed native code is
pushed to a client, we calculate a compatibility version from the Cordova platform and plugin
versions, and only download a new version to a device when there is an exact match. This
means any change to the list of plugins, or updating to a Meteor release which contains a
new platform version, will block hot code push to existing mobile clients until the app has
been updated from the store.

Something else to keep in mind is that your server-side code should be prepared to handle
requests from older client versions, which may not yet have been updated. As you make
changes to your data schema or publication functions for example, you may want to reflect
on how this will impact backwards compatibility.

Configuring your server


As mentioned before, mobile apps need to be able to connect to a server to support hot
code push. In production, you will need to specify which server to connect to when building
the app using the --server option. The specified server address is used to set ROOT_URL in
__meteor_runtime_config__ , which is defined as part of the generated index.html in the

app bundle.

In addition, you will need to configure the server with the right connection address. This
happens automatically if you're using meteor deploy to deploy to Galaxy, but when
deploying to your own server you'll have to make sure to define the ROOT_URL environment
variable there. (For Meteor Up, you can configure this in mup.json .)

The reason this is needed is because updates delivered through hot code push replace the
initially bundled index.html with a freshly generated one. If the ROOT_URL on your server
hasn't been set, it defaults to localhost:3000 , and this would leave the app unable to
connect to the server, both for data loading and for receiving further hot code pushes. In
Meteor 1.3, we protect against this by blocking updates that would change the ROOT_URL to
localhost , but the consequence of this is that hot code push is disabled until you configure

ROOT_URL correctly.

Recovering from faulty versions


Hot code pushing updated JavaScript code to a device could accidentally push code
containing errors, which might leave users with a broken app (a 'white screen of death', in
the worst case), and could even disable hot code push (because the code that makes a

219
Mobile

connection to the server may no longer run).

To avoid this, we try to detect faulty versions and revert to the last known good version when
this happens. The way detection works is that we expect all Meteor.startup() callbacks to
complete within a set period of time. If this doesn't happen we consider the version faulty
and will rollback the update. Unless the version on the server has been updated in the
meantime, the server will try to hot code push the faulty version again. Therefore, we
blacklist faulty versions on the device so we know not to retry.

By default, the startup timeout is set to 20 seconds. If your app needs more time to startup
(or considerably less), you can use App.setPreference to set WebAppStartupTimeout to
another value.

Native features with Cordova plugins


Cordova comes with a plugin architecture that opens up access to features not usually
available to web apps. Plugins are installable add-ons that contain both JavaScript and
native code, which allows them to translate calls from your web app to platform-specific
APIs.

The Apache Cordova project maintains a set of core plugins that provide access to various
native device features such as the camera, contacts, or access to the file system. But
anyone can write a Cordova plugin to do basically anything that can be done from native
code, and many third-party plugins are available. You can search for plugins on the Cordova
website or directly on npm.

Be warned however, that although the core plugins are generally well maintained and up to
date with the rest of Cordova, the quality of third-party plugins can be a bit of a gamble. You
also have to make sure the plugin you want to use is compatible with the Cordova platform
versions Meteor bundles.

Installing plugins
Plugins are identified by a name, which is generally the same as their npm package name.
The current convention is for plugin names to start with cordova-plugin- , but not all third-
party plugins adhere to this.

You can add Cordova plugins to your project either directly, or as a dependency of a Meteor
package.

If you want to add a plugin to your project directly, you use the same meteor add command
you use for Meteor packages, but with a cordova: prefix:

220
Mobile

meteor add cordova:cordova-plugin-camera@1.2.0

In contrast to Meteor packages, you'll have to specify the exact version of the plugin. This
can be a bit of a pain because you first need to look up what the most recent (compatible)
version of a plugin is before you can add it.

A Meteor package can register a dependency on a Cordova plugin with the


Cordova.depends() syntax. For example, a Meteor package that depends on the Cordova

camera plugin would add the following to its package.js :

Cordova.depends({
'cordova-plugin-camera': '1.2.0'
});

This means adding the Meteor package to your project would also install the specified
Cordova plugin.

Note: If multiple Meteor packages add the same Cordova plugin but at different
versions, there is no clear way of telling which version will end up being installed.
Plugins added to your project directly however, will always override versions of the
same plugin added as a dependency of packages.

Because installing plugins into a Cordova project already containing plugins can lead to
indeterminate results, Meteor will remove and add back all plugins whenever a change to
any of the plugins in your project is made.

Cordova downloads plugins from npm, and caches them (in ~/.cordova/lib/npm_cache ) so
they don't have to be downloaded repeatedly if you rebuild or use them again in another
project.

221
Mobile

Making sure a plugin is compatible with the bundled


Cordova platform versions
Because there is a tight coupling between plugin versions and Cordova platform
versions, you may encounter build time or runtime errors as a result of incompatible
plugins. If this happens, you will have to install a different plugin version, or it may turn
out a plugin is not (yet) compatible with the Cordova platform versions we bundle.

In order to help with this, we pin core plugins to a minimum version known to work with
the Cordova versions we bundle. This mechanism doesn't apply to third-party plugins
however, so you'll have to assess compatibility for these yourself.

There is ongoing work in the Cordova project that will improve this situation and make it
easier for plugins to specify their platform dependencies, so Cordova can determine
compatible versions.

Setting plugin parameters


Some Cordova plugins require certain parameters to be set as part of the build process. For
example, com-phonegap-plugins-facebookconnect requires you to specify an APP_ID and
APP_NAME . You can set these using App.configurePlugin in your mobile-config.js.

Installing a plugin from Git


Alternatively, if unreleased changes have been made to a plugin you'd like to use, you can
also have Cordova download plugin code from a Git repository. Note that this will clone the
plugin repository on every rebuild however, so this can be rather slow and should be avoided
where possible. In contrast to default Cordova, Meteor requires you to specify the exact SHA
hash for a commit, rather than allow you to refer to a branch or tag. This is done to
guarantee repeatable builds and also avoids unnecessary reinstallation of all plugins
because as long as the SHA is the same we know nothing has changed.

The syntax to add a plugin from Git is kind of awkward. The name (the part before the @ ) is
the plugin ID and will have to match what is specified in the plugin's plugin.xml . Instead of
a version, you specify a URL to a Git repository with the SHA hash as an anchor (the part
after the # ):

meteor add cordova:com.phonegap.plugins.facebookconnect@https://github.com/Wizcorp/pho


negap-facebook-plugin.git#5dbb1583168558b4447a13235283803151cb04ec

Meteor packages can also depend on plugins downloaded from Git:

222
Mobile

Cordova.depends({
'com.phonegap.plugins.facebookconnect': 'https://github.com/Wizcorp/phonegap-faceb
ook-plugin.git#5dbb1583168558b4447a13235283803151cb04ec'
});

Installing a plugin from the local file system


Finally, especially if you're developing your own plugin, installing it from the local filesystem
can be a convenient way to keep up with changes you make to plugin code. The downside
of this is that Meteor will reinstall all plugins on every build however, so this could really slow
things down. We do add local plugins with the --link option however, so Cordova will try to
install the plugin's files using symlinks instead of copying them, which means changes to
files will be reflected in the generated native project (e.g. an Xcode project) and may not
require a rebuild.

You install plugins from the local file system by specifying a file:// URL, which gets
interpreted relative to the project directory:

meteor add cordova:cordova-plugin-underdevelopment@file://../plugins/cordova-plugin-un


derdevelopment

Meteor packages can also depend on plugins installed from the local file system, although
this probably only makes sense for local packages:

Cordova.depends({
'cordova-plugin-underdevelopment': 'file://../plugins/cordova-plugin-underdevelopm
ent'
});

Removing directly installed plugins


You can remove a previously added plugin using meteor remove :

meteor remove cordova:cordova-plugin-camera


meteor remove cordova:com.phonegap.plugins.facebookconnect
meteor remove cordova:cordova-plugin-underdevelopment

Using plugins

223
Mobile

You should wrap any functionality which relies on a Cordova plugin in a Meteor.startup()
block to make sure the plugin has been fully initialized (by listening to the deviceready
event). For example, when using the Cordova geolocation plugin:

// The plugin may not have been initialized here


navigator.geolocation.getCurrentPosition(success);

Meteor.startup(function() {
// Here we can be sure the plugin has been initialized
navigator.geolocation.getCurrentPosition(success);
});

Detecting Cordova in your JavaScript code


Just as you can use Meteor.isServer and Meteor.isClient to separate your client-side and
server-side code, you can use Meteor.isCordova to separate your Cordova-specific code
from the rest of your code.

if (Meteor.isServer) {
console.log("Printed on the server");
}

if (Meteor.isClient) {
console.log("Printed in browsers and mobile apps");
}

if (Meteor.isCordova) {
console.log("Printed only in mobile Cordova apps");
}

In addition, packages can include a different set of files for Cordova builds and browser
builds with addFiles :

api.addFiles('foo.js', 'web.cordova') : includes foo.js in only Cordova builds.

api.addFiles('bar.js', 'web.browser') : includes bar.js in only browser builds.

api.addFiles('baz.js', 'web') : includes baz.js in all client builds.

The same syntax can be used for api.use , api.imply , and api.export .

Accessing local files and remote resources


As a web app, Cordova apps are subject to various security mechanisms designed to
protect the integrity of your code and to avoid certain types of attacks. Which security
mechanisms are in use may depend on the type and version of the web view your app runs

224
Mobile

in. In addition, Cordova itself, and in some cases the OS, adds different levels of access
control that may also affect what content can and cannot be loaded. All this can make it fairly
confusing to understand why something is not working, and even harder to understand the
security implications of the various ways of configuring these mechanisms.

Local files
Because the Cordova integration in Meteor does not serve your app from file:// URLs,
access to local files through file:// URLs is not allowed either due to the same-origin
policy.

The file serving mechanism used in Meteor allows for local file access through URLs of the
form http://localhost:<port>/local-filesystem/<path> ) however. You can construct these
file system URLs manually, or use WebAppLocalServer.localFileSystemUrl() to convert
file:// URLs. You can use this to convert URLs received from plugins like cordova-

plugin-file and cordova-plugin-camera for example.

Domain whitelisting
Cordova controls access to external domains through a whitelisting mechanism, which is
implemented as cordova-plugin-whitelist in the version of Cordova we bundle.

In Meteor, you use App.accessRule in mobile-config.js to set additional rules. (These


correspond to <access> , <allow-navigation> and <allow-intent> tags in the generated
config.xml .)

On iOS, these settings also control Application Transport Security (ATS), which is an
OS level mechanism to enforce security best practices new to iOS 9. If the server
you're connecting to does not (yet) fulfill these requirements, you can use additional
options to override them for specific domains:

App.accessRule('https://domain.com', {
'minimum-tls-version': 'TLSv1.0',
'requires-forward-secrecy': false,
});

By default, Cordova apps in Meteor are only allowed access to localhost (the device itself,
to serve the app from) and the server your app connects to for data loading and hot code
push (either an automatically detected IP address an explicitly configured mobile server
domain). These restrictions also apply to loading files in iframes and to opening files in other
apps (including the mobile browser).

225
Mobile

Note that these restrictions mean you will have to explicitly allow loading data: URLs.
For example, to allow loading data: URLs in iframes you would add:

App.accessRule('data:*', { type: 'navigation' });

Content Security Policy (CSP)


In addition to the domain whitelisting mechanism Cordova implements, the web view itself
may also enforce access rules through Content Security Policy (CSP). For now, Meteor adds
a permissive <meta http-equiv="Content-Security-Policy" content="..." header to the
generated index page. We may want to allow more fine grained control in the future (through
integrating with the browser-policy package for instance.)

Cross-Origin Resource Sharing (CORS)


What is often confusing to people is that setting App.accessRule is not enough to allow
access to remote resources. While domain whitelisting allows the client to control which
domains it can connect to, additional restrictions based on the same-origin policy also apply.
By default, web views will not allow cross-origin HTTP requests initiated from JavaScript for
instance, so you will likely run into this when using XMLHttpRequest .

To get around these restrictions, you'll have to use what is known as Cross-Origin Resource
Sharing (CORS). In contrast to the whitelisting mechanism configured on the client, CORS
relies on headers set by the server. In other words, in order to allow access to a remote
resource, you may have to make configuration changes on the server, such as setting a
Access-Control-Allow-Origin header.

Configuring your app


Meteor reads a mobile-config.js file in the root of your app directory during build, and uses
the settings specified there to generate Cordova's config.xml .

Metadata

App.info({
id: 'com.meteor.examples.todos',
name: 'Todos',
version: "0.0.1"
});

226
Mobile

Preferences

App.setPreference('BackgroundColor', '0xff0000ff');
App.setPreference('Orientation', 'default');
App.setPreference('Orientation', 'all', 'ios');

Refer to the preferences section of the Cordova documentation for more information about
supported options.

App icons and launch screens


Although Meteor includes a standard set of app icons and launch screens, you'll most likely
want to configure your own images.

You configure these images with App.icons and App.launchScreens , which both use names
to refer to the various supported image sizes (see API documentation).

For iOS, you can also refer to the Icon and image sizes in the iOS Human Interface
Guidelines for more information about the way these different sizes are used.

Advanced build customization


There is a special top-level directory named cordova-build-override/ that allows you to
override, in an ad-hoc way, parts of your Cordova project that Meteor generates for you in
the .meteor/local/cordova-build directory. The entire file tree of this directory will be cp -R
(copied overwriting existing files) to the Cordova project right before the build and
compilation step.

The problem with this mechanism is that it overrides complete files, so it is not a good
solution for customizing config.xml . Replacing the generated version with your own file
means you lose all configuration information set by the build process and by installed
plugins, which will likely break your app.

If you need to customize configuration files, a workaround is to create a dummy Cordova


plugin. In its plugin.xml , you can specify a config-file element to selectively change
parts of configuration files, including config.xml .

We recommend using these approaches only if absolutely required and if your


customizations can not be handled by standard configuration options.

Deploying to production

227
Mobile

Building for production


Use meteor build <build-output-directory> --server <host>:<port> to build your app for
production.

The <host> and <port> should be the address of the server you want your app to connect
to.

This will generate a directory at <build-output-directory> , which includes a server bundle


tarball and the project source for each targeted mobile platform in the /ios and /android
directories.

You can pass --server-only to only build the server bundle. This allows you to build your
app without installing the mobile SDKs on the build machine. This is useful if you use an
automated deployment setup for instance. (If you remove the mobile platforms before
building instead, hot code push will be disabled because the assets for Cordova included in
the server bundle will not be generated.)

iOS App Store


In order to build your app for iOS, you will need to configure your app with at least a version
number, and the required set of app icons and launch screens.

After running meteor build you can open the generated Xcode project in Xcode:

cd <build-output-directory>/ios/project
open MyApp.xcodeproj

From this point on, the process for building the app archive and submitting it to the App
Store is the same as it would be for any other iOS app. Please refer to Apple's
documentation for further details.

Android Play Store


In order to build your app for Android, you will need to configure your app with at least a
version number, and the required set of app icons and launch screens.

After running meteor build the generated APK will be copied from the <build-output-
directory>/android/project/build/outputs/apk directory to <build-output-

directory>/android/release-unsigned.apk .

Before submitting the APK(s) to the Play Store, you will need to sign the APK and run
zipalign on it to optimize the archive.

228
Mobile

(See the Android developer documentation for more details about the app signing
procedure.)

To sign your app, you'll need a private key. This key lets you publish and update your app. If
you haven't made a key for this app yet, run:

keytool -genkey -alias your-app-name -keyalg RSA -keysize 2048 -validity 10000

Optionally, you can specify --keystore to use a different keystore. Don't forget to specify
the same keystore when signing the APK.

Note: Ensure that you have secure backups of your keystore ( ~/.keystore is the
default). If you publish an app to the Play Store and then lose the key with which you
signed your app, you will not be able to publish any updates to your app, since you
must always sign all versions of your app with the same key.

Now, you can sign the APK:

cd ~/build-output-directory/android/
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 release-unsigned.apk your-app-n
ame

Next, you can run zipalign on it to optimize the APK:

$ANDROID_HOME/build-tools/<build-tools-version>/zipalign 4 release-unsigned.apk <your-


app-name>.apk

From this point on, the process for submitting the app to the Play Store is the same as it
would be for any other Android app. <your-app-name>.apk is the APK to upload to the store.
Learn more by visiting https://play.google.com/apps/publish.

Submitting an app using Crosswalk to to Play Store


Because Crosswalk bundles native code for Chromium, you will end up with APKs for both
ARM and x86. You can find the generated APKs in the <build-output-
directory>/android/project/build/outputs/apk directory.

You will have to sign and zipalign both APKs. You will also have to submit both to the Play
Store, see submitting multiple APKs for more information.

229
Build system

The Meteor build system is the actual command line tool that you get when you install
Meteor. You run it by typing the meteor command in your terminal, possibly followed by a
set of arguments. Read the docs about the command line tool or type meteor help in your
terminal to learn about all of the commands.

What does it do?


The Meteor build tool is what compiles, runs, deploys, and publishes all of your Meteor apps
and packages. It's Meteor's built-in solution to the problems also solved by tools like Grunt,
Gulp, Webpack, Browserify, Nodemon, and many others, and uses many popular Node.js
tools like Babel and UglifyJS internally to enable a seamless experience.

Reloads app on file change


When you run meteor , the tool starts up, and you should leave it running continuously while
developing your app. The tool automatically detects any relevant file changes and
recompiles the necessary changes, restarting your client or server environment if needed.

Compiles files with build plugins


The main function of the Meteor build tool is to run "build plugins". These plugins define
different parts of your app build process. Meteor puts heavy emphasis on reducing or
removing build configuration files, so you won't see any large build process config files like
you would in Gulp or Webpack. The Meteor build process, and file load order, is configured
almost entirely through adding and removing packages to your app and putting files in
specially named directories. For example, to get all of the newest stable ES2015 JavaScript
features in your app, you just add the ecmascript package. This package provides support
for ES2015 modules, which gives you even more fine grained control over file load order
using ES2015 import and export . As new Meteor releases add new features to this
package you just get them for free.

Combines and minifies code


Another important feature of the Meteor build tool is that it automatically concatenates and
minifies all of your files in production mode. This is enabled by the standard-minifier-js
and standard-minifier-css packages, which are in all Meteor apps by default. If you need
different minification behavior, you can replace these packages. Below, we'll talk about how
to switch out a minifier to add PostCSS to your build process.

Development vs. production

230
Build system

Running an app in development is all about fast iteration time. All kinds of different parts of
your app are handled differently and instrumented to enable better reloads and debugging.
In production, the app is reduced to just the necessary code, and functions like a regular
Node.js app. Therefore, you shouldn't run your app in production by running the meteor
command. Instead, follow the directions in the production deployment article.

JavaScript transpilation
These days, the landscape of JavaScript tools and frameworks is constantly shifting, and the
language itself is evolving just as rapidly. It's no longer reasonable to wait for web browsers
to implement the language features you want to use. Most JavaScript development
workflows rely on compiling code to work on the lowest common denominator of
environments, while letting you use the newest features in development. Meteor has support
for some of the most popular tools out of the box.

ES2015+ (recommended)
The ecmascript package (which is installed into all new apps and packages by default, but
can be removed), allows support for many ES2015 features. We recommend using it. You
can read more about it in the Code Style article.

CoffeeScript
While we recommend using ES2015 with the ecmascript package as the best development
experience for Meteor, everything in the platform is 100% compatible with CoffeeScript and
many people in the Meteor community prefer it.

All you need to do to use CoffeeScript is add the right Meteor package:

meteor add coffeescript

All code written in CoffeeScript compiles to JavaScript under the hood, and is completely
compatible with any code in other packages that is written in JS or ES2015.

Templates and HTML


Since Meteor uses client-side rendering for your app's UI, all of your HTML code, UI
components, and templates need to be compiled to JavaScript. There are a few options at
your disposal to write your UI code.

231
Build system

Blaze HTML templates


The aptly named blaze-html-templates package that comes with every new Meteor app by
default compiles your .html files written using Spacebars into Blaze-compatible JavaScript
code. You can also add blaze-html-templates to any of your packages to compile template
files located in the package.

Read about how to use Blaze and Spacebars in the Blaze article.

Blaze Jade templates


If you don't like the Spacebars syntax Meteor uses by default and want something more
concise, you can give Jade a try by using dalgard:jade . This package will compile all files
in your app with the .jade extension into Blaze-compatible code, and can be used side-by-
side with blaze-html-templates if you want to have some of your code in Spacebars and
some in Jade.

JSX for React


If you're building your app's UI with React, currently the most popular way to write your UI
components involves JSX, an extension to JavaScript that allows you to type HTML tags
that are converted to React DOM elements. JSX code is handled automatically by the
ecmascript package.

Other options for React


If you want to use React but don't want to deal with JSX and prefer a more HTML-like
syntax, there are a few community options available. One that stands out in particular is
Blaze-React, which simulates the entire Blaze API using React as a rendering engine.

Angular templates
If you would like to write your UI in Angular, you will need to switch out Meteor's Blaze
template compiler which comes by default with the Angular one. Read about how to do this
in the Angular-Meteor tutorial.

CSS and CSS pre-processors


All your CSS style files will processed using Meteor's default file load order rules along with
any import statements and concatenated, and in a production build also minified. However,
it's no secret that writing plain CSS can often be a hassle as there's no way to share

232
Build system

common CSS code between different selectors or have a consistent color scheme between
different elements. CSS compilers, or pre-processors, solve these issues by adding extra
features on top of the CSS language like variables, mixins, math, and more, and in some
cases also significantly change the syntax of CSS to be easier to read and write.

Sass, Less, or Stylus?


There are three CSS pre-processors that are particularly popular right now:

1. Sass
2. Less.js
3. Stylus

They all have their pros and cons, and different people have different preferences, just like
with JavaScript transpiled languages. The most popular one at the time of writing seems to
be Sass with the SCSS syntax. Popular CSS frameworks like Bootstrap 4 and more are
switching to Sass, and the C++ LibSass implementation appears to be faster than some of
the other compilers available.

CSS framework compatibility should be a primary concern when picking a pre-processor,


because a framework written with Less won't be compatible with one written in Sass.

Source vs. import files


An important feature shared by all of the available CSS pre-processors is the ability to import
files. This lets you split your CSS into smaller pieces, and provides a lot of the same benefits
that you get from JavaScript modules:

1. You can control the load order of files by encoding dependencies through imports, since
the load order of CSS matters.
2. You can create reusable CSS "modules" that just have variables and mixins and don't
actually generate any CSS.

In Meteor, each of your .scss , .less , or .styl source files will be one of two types:
"source" or "import".

A "source" file is evaluated eagerly and adds its compiled form to the CSS of the app
immediately.

An "import" file is evaluated only if imported from some other file and can be used to share
common mixins and variables between different CSS files in your app.

Read the documentation for each package listed below to see how to indicate which files are
source files vs. imports.

233
Build system

Importing styles
In all three Meteor supported CSS pre-processors you can import other style files from both
relative and absolute paths in your app and from both npm and Meteor Atmosphere
packages.

@import '../stylesheets/colors.less'; // a relative path


@import '{}/imports/ui/stylesheets/button.less'; // absolute path with `{}` syntax

You can also import CSS from a JavaScript file if you have the ecmascript package
installed:

import '../stylesheets/styles.css';

When importing CSS from a JavaScript file, that CSS is not bundled with the rest of the
CSS processed with the Meteor Build tool, but instead is put in your app's <head> tag
inside <style>...</style> after the main concatenated CSS file.

Importing styles from an Atmosphere package using the {} package name syntax:

@import '{my-package:pretty-buttons}/buttons/styles.import.less';

CSS files in an Atmosphere package are declared with api.addFiles , and therefore
will be eagerly evaluated, and automatically bundled with all the other CSS in your app.

Importing styles from an npm package using the {} syntax:

@import '{}/node_modules/npm-package-name/button.less';

import 'npm-package-name/stylesheets/styles.css';

For more examples and details on importing styles and using @imports with packages see
the Using Packages article.

Sass
The best Sass build plugin for Meteor is fourseven:scss .

Less
Less is maintained as a Meteor core package called less .

234
Build system

Stylus
Stylus is maintained as a Meteor core package called stylus .

PostCSS and Autoprefixer


In addition to CSS pre-processors like Sass, Less, and Stylus, there is now an ecosystem of
CSS post-processors. Regardless of which CSS pre-processor you use, a post-processor
can give you additional benefits like cross-browser compatibility.

The most popular CSS post-processor right now is PostCSS, which supports a variety of
plugins. Autoprefixer is perhaps the most useful plugin, since it enables you to stop worrying
about browser prefixes and compatibility and write standards-compliant CSS. No more
copying 5 different statements every time you want a CSS gradient - you can just write a
standard gradient without any prefixes and Autoprefixer handles it for you.

Currently, Meteor doesn't have a separate build step for post-processing CSS, so the only
way to integrate it is to build it into the minifier. Thankfully, there is a community package that
has integrated PostCSS with plugin support into a replacement for Meteor's standard
minification package.

juliancwirko:postcss
Use the package juliancwirko:postcss to your app to enable PostCSS for your Meteor app.
To do so, we remove the standard CSS minifier and replace it with the postcss package:

meteor remove standard-minifier-css


meteor add juliancwirko:postcss

Then we can install any npm CSS processing packages that we'd like to use and reference
them from a postcss section of our package.json . In the Todos example app, we use
autoprefixer package to increase browser support:

{
"devDependencies": {
"autoprefixer": "^6.3.1"
},
"postcss": {
"plugins": {
"autoprefixer": {"browsers": ["last 2 versions"]}
}
}
}

235
Build system

After doing the above, you'll need to ensure you npm install and restart the meteor
process running your app to make sure the PostCSS system has had a chance to set itself
up.

Minification
The current best practice for deploying web production applications is to concatenate and
minify all of your app assets. This lets you add all of the comments and whitespace you want
to your source code, and split it into as many files as is necessary without worrying about
app performance.

Every Meteor app comes with production minification by default with the standard-minifier-
js and standard-minifier-css packages. These minifiers go to some extra effort to do a

good job - for example, Meteor automatically splits up your files if they get too big to
maintain support for older versions of Internet Explorer which had a limit on the number of
CSS rules per file.

Minification usually happens when you meteor deploy or meteor build your app. If you
have an error in production that you suspect is related to minification, you can run the
minified version of your app locally with meteor --production .

Build plugins
The most powerful feature of Meteor's build system is the ability to define custom build
plugins. If you find yourself writing scripts that mangle one type of file into another, merge
multiple files, or something else, it's likely that these scripts would be better implemented as
a build plugin. The ecmascript , templating , and coffeescript packages are all
implemented as build plugins, so you can replace them with your own versions if you want
to!

Read the documentation about build plugins.

Types of build plugins


There are three types of build plugins supported by Meteor today:

1. Compiler plugin - compiles source files (LESS, CoffeeScript) into built output (JS, CSS,
asset files, and HTML). Only one compiler plugin can handle a single file extension.
2. Minifier plugin - compiles lots of built CSS or JS files into one or more minified files, for
example standard-minifiers . Only one minifier can handle each of js and css .
3. Linter plugin - processes any number of files, and can print lint errors. Multiple linters

236
Build system

can process the same files.

Writing your own build plugin


Writing a build plugin is a very advanced task that only the most advanced Meteor users
should get into. The best place to start is to copy a different plugin that is the most similar to
what you are trying to do. For example, if you wanted to make a new CSS compiler plugin,
you could fork the less package; if you wanted to make your own JS transpiler, you could
fork ecmascript . A good example of a linter is the jshint package, and for a minifier you
can look at standard-minifiers-js and standard-minifiers-css .

Caching
The best way to make your build plugin fast is to use caching anywhere you can - the best
way to save time is to do less work! Check out the documentation about CachingCompiler to
learn more. It's used in all of the above examples, so you can see how to use it by looking at
them.

237
Security

After reading this guide, you'll know:

1. The security surface area of a Meteor app.


2. How to secure Meteor Methods, publications, and source code.
3. Where to store secret keys in development and production.
4. How to follow a security checklist when auditing your app.

Introduction
Securing a web application is all about understanding security domains and understanding
the attack surface between these domains. In a Meteor app, things are pretty simple:

1. Code that runs on the server can be trusted.


2. Everything else: code that runs on the client, data sent through Method and publication
arguments, etc, can't be trusted.

In practice, this means that you should do most of your security and validation on the
boundary between these two domains. In simple terms:

1. Validate and check all inputs that come from the client.
2. Don't leak any secret information to the client.

Concept: Attack surface


Since Meteor apps are often written in a style that puts client and server code together, it's
extra important to be aware what is running on the client, what is running on the server, and
what the boundaries are. Here's a complete list of places security checks need to be done in
a Meteor app:

1. Methods: Any data that comes in through Method arguments needs to be validated,
and Methods should not return data the user shouldn't have access to.
2. Publications: Any data that comes in through publication arguments needs to be
validated, and publications should not return data the user shouldn't have access to.
3. Served files: You should make sure none of the source code or configuration files
served to the client have secret data.

Each of these points will have their own section below.

Avoid allow/deny

238
Security

In this guide, we're going to take a strong position that using allow or deny to run MongoDB
queries directly from the client is not a good idea. The main reason is that it is hard to follow
the principles outlined above. It's extremely difficult to validate the complete space of
possible MongoDB operators, which could potentially grow over time with new versions of
MongoDB.

There have been several articles about the potential pitfalls of accepting MongoDB update
operators from the client, in particular the Allow & Deny Security Challenge and its results,
both on the Discover Meteor blog.

Given the points above, we recommend that all Meteor apps should use Methods to accept
data input from the client, and restrict the arguments accepted by each Method as tightly as
possible.

Here's a code snippet to add to your server code which disables client-side updates on a
collection. This will make sure no other part of your app can use allow :

// Deny all client-side updates on the Lists collection


Lists.deny({
insert() { return true; },
update() { return true; },
remove() { return true; },
});

Methods
Methods are the way your Meteor server accepts inputs and data from the outside world, so
it's natural that they are the most important topic for security. If you don't properly secure
your Methods, users can end up modifying your database in unexpected ways - editing other
people's documents, deleting data, or messing up your database schema causing the app to
crash.

Validate all arguments


It's much easier to write clean code if you can assume your inputs are correct, so it's
valuable to validate all Method arguments before running any actual business logic. You
don't want someone to pass a data type you aren't expecting and cause unexpected
behavior.

Consider that if you are writing unit tests for your Methods, you would need to test all
possible kinds of input to the Method; validating the arguments restricts the space of inputs
you need to unit test, reducing the amount of code you need to write overall. It also has the

239
Security

extra bonus of being self-documenting; someone else can come along and read the code to
find out what kinds of parameters a Method is looking for.

Just as an example, here's a situation where not checking arguments can be disastrous:

Meteor.methods({
removeWidget(id) {
if (! this.userId) {
throw new Meteor.Error('removeWidget.unauthorized');
}

Widgets.remove(id);
}
});

If someone comes along and passes a non-ID selector like {} , they will end up deleting the
entire collection.

mdg:validated-method
To help you write good Methods that exhaustively validate their arguments, we've written a
simple wrapper package for Methods that enforces argument validation. Read more about
how to use it in the Methods article. The rest of the code samples in this article will assume
that you are using this package. If you aren't, you can still apply the same principles but the
code will look a little different.

Don't pass userId from the client


The this context inside every Meteor Method has some useful information about the
current connection, and the most useful is this.userId . This property is managed by the
DDP login system, and is guaranteed by the framework itself to be secure following widely-
used best practices.

Given that the user ID of the current user is available through this context, you should never
pass the ID of the current user as an argument to a Method. This would allow any client of
your app to pass any user ID they want. Let's look at an example:

240
Security

// #1: Bad! The client could pass any user ID and set someone else's name
setName({ userId, newName }) {
Meteor.users.update(userId, {
$set: { name: newName }
});
}

// #2: Good, the client can only set the name on the currently logged in user
setName({ newName }) {
Meteor.users.update(this.userId, {
$set: { name: newName }
});
}

The only times you should be passing any user ID as an argument are the following:

1. This is a Method only accessible by admin users, who are allowed to edit other users.
See the section about user roles to learn how to check that a user is in a certain role.
2. This Method doesn't modify the other user, but uses it as a target; for example, it could
be a Method for sending a private message, or adding a user as a friend.

One Method per action


The best way to make your app secure is to understand all of the possible inputs that could
come from an untrusted source, and make sure that they are all handled correctly. The
easiest way to understand what inputs can come from the client is to restrict them to as
small of a space as possible. This means your Methods should all be specific actions, and
shouldn't take a multitude of options that change the behavior in significant ways. The end
goal is that you can easily look at each Method in your app and validate or test that it is
secure. Here's a secure example Method from the Todos example app:

241
Security

export const makePrivate = new ValidatedMethod({


name: 'lists.makePrivate',
validate: new SimpleSchema({
listId: { type: String }
}).validator(),
run({ listId }) {
if (!this.userId) {
throw new Meteor.Error('lists.makePrivate.notLoggedIn',
'Must be logged in to make private lists.');
}

const list = Lists.findOne(listId);

if (list.isLastPublicList()) {
throw new Meteor.Error('lists.makePrivate.lastPublicList',
'Cannot make the last public list private.');
}

Lists.update(listId, {
$set: { userId: this.userId }
});

Lists.userIdDenormalizer.set(listId, this.userId);
}
});

You can see that this Method does a very specific thing - it just makes a single list private.
An alternative would have been to have a Method called setPrivacy , which could set the
list to private or public, but it turns out that in this particular app the security considerations
for the two related operations - makePrivate and makePublic - are very different. By
splitting our operations into different Methods, we make each one much clearer. It's obvious
from the above Method definition which arguments we accept, what security checks we
perform, and what operations we do on the database.

However, this doesn't mean you can't have any flexibility in your Methods. Let's look at an
example:

242
Security

const Meteor.users.methods.setUserData = new ValidatedMethod({


name: 'Meteor.users.methods.setUserData',
validate: new SimpleSchema({
fullName: { type: String, optional: true },
dateOfBirth: { type: Date, optional: true },
}).validator(),
run(fieldsToSet) {
Meteor.users.update(this.userId, {
$set: fieldsToSet
});
}
});

The above Method is great because you can have the flexibility of having some optional
fields and only passing the ones you want to change. In particular, what makes it possible for
this Method is that the security considerations of setting one's full name and date of birth are
the same - we don't have to do different security checks for different fields being set. Note
that it's very important that the $set query on MongoDB is generated on the server - we
should never take MongoDB operators as-is from the client, since they are hard to validate
and could result in unexpected side effects.

Refactoring to reuse security rules


You might run into a situation where many Methods in your app have the same security
checks. This can be simplified by factoring out the security into a separate module, wrapping
the Method body, or extending the Mongo.Collection class to do security inside the insert ,
update , and remove implementations on the server. However, implementing your client-

server communication via specific Methods is still a good idea rather than sending arbitrary
update operators from the client, since a malicious client can't send an update operator

that you didn't test for.

Rate limiting
Just like REST endpoints, Meteor Methods can easily be called from anywhere - a malicious
program, script in the browser console, etc. It is easy to fire many Method calls in a very
short amount of time. This means it can be easy for an attacker to test lots of different inputs
to find one that works. Meteor has built-in rate limiting for password login to stop password
brute-forcing, but it's up to you to define rate limits for your other Methods.

In the Todos example app, we use the following code to set a basic rate limit on all Methods:

243
Security

// Get list of all method names on Lists


const LISTS_METHODS = _.pluck([
insert,
makePublic,
makePrivate,
updateName,
remove,
], 'name');

// Only allow 5 list operations per connection per second


DDPRateLimiter.addRule({
name(name) {
return _.contains(LISTS_METHODS, name);
},

// Rate limit per connection ID


connectionId() { return true; }
}, 5, 1000);

This will make every Method only callable 5 times per second per connection. This is a rate
limit that shouldn't be noticeable by the user at all, but will prevent a malicious script from
totally flooding the server with requests. You will need to tune the limit parameters to match
your app's needs.

Publications
Publications are the primary way a Meteor server can make data available to a client. While
with Methods the primary concern was making sure users can't modify the database in
unexpected ways, with publications the main issue is filtering the data being returned so that
a malicious user can't get access to data they aren't supposed to see.

You can't do security at the rendering layer


In a server-side-rendered framework like Ruby on Rails, it's sufficient to simply not display
sensitive data in the returned HTML response. In Meteor, since the rendering is done on the
client, an if statement in your HTML template is not secure; you need to do security at the
data level to make sure that data is never sent in the first place.

Rules about Methods still apply


All of the points above about Methods apply to publications as well:

1. Validate all arguments using check or aldeed:simple-schema .


2. Never pass the current user ID as an argument.

244
Security

3. Don't take generic arguments; make sure you know exactly what your publication is
getting from the client.
4. Use rate limiting to stop people from spamming you with subscriptions.

Always restrict fields


Mongo.Collection#find has an option called fields which lets you filter the fields on the

fetched documents. You should always use this in publications to make sure you don't
accidentally publish secret fields.

For example, you could write a publication, then later add a secret field to the published
collection. Now, the publication would be sending that secret to the client. If you filter the
fields on every publication when you first write it, then adding another field won't
automatically publish it.

// #1: Bad! If we add a secret field to Lists later, the client


// will see it
Meteor.publish('lists.public', function () {
return Lists.find({userId: {$exists: false}});
});

// #2: Good, if we add a secret field to Lists later, the client


// will only publish it if we add it to the list of fields
Meteor.publish('lists.public', function () {
return Lists.find({userId: {$exists: false}}, {
fields: {
name: 1,
incompleteCount: 1,
userId: 1
}
});
});

If you find yourself repeating the fields often, it makes sense to factor out a dictionary of
public fields that you can always filter by, like so:

// In the file where Lists is defined


Lists.publicFields = {
name: 1,
incompleteCount: 1,
userId: 1
};

Now your code becomes a bit simpler:

245
Security

Meteor.publish('lists.public', function () {
return Lists.find({userId: {$exists: false}}, {
fields: Lists.publicFields
});
});

Publications and userId


The data returned from publications will often be dependent on the currently logged in user,
and perhaps some properties about that user - whether they are an admin, whether they
own a certain document, etc.

Publications are not reactive, and they only re-run when the currently logged in userId
changes, which can be accessed through this.userId . Because of this, it's easy to
accidentally write a publication that is secure when it first runs, but doesn't respond to
changes in the app environment. Let's look at an example:

246
Security

// #1: Bad! If the owner of the list changes, the old owner will still see it
Meteor.publish('list', function (listId) {
check(listId, String);

const list = Lists.findOne(listId);

if (list.userId !== this.userId) {


throw new Meteor.Error('list.unauthorized',
'This list doesn\'t belong to you.');
}

return Lists.find(listId, {
fields: {
name: 1,
incompleteCount: 1,
userId: 1
}
});
});

// #2: Good! When the owner of the list changes, the old owner won't see it anymore
Meteor.publish('list', function (listId) {
check(listId, String);

return Lists.find({
_id: listId,
userId: this.userId
}, {
fields: {
name: 1,
incompleteCount: 1,
userId: 1
}
});
});

In the first example, if the userId property on the selected list changes, the query in the
publication will still return the data, since the security check in the beginning will not re-run.
In the second example, we have fixed this by putting the security check in the returned query
itself.

Unfortunately, not all publications are as simple to secure as the example above. For more
tips on how to use reywood:publish-composite to handle reactive changes in publications,
see the data loading article.

Passing options

247
Security

For certain applications, for example pagination, you'll want to pass options into the
publication to control things like how many documents should be sent to the client. There are
some extra considerations to keep in mind for this particular case.

1. Passing a limit: In the case where you are passing the limit option of the query from
the client, make sure to set a maximum limit. Otherwise, a malicious client could request
too many documents at once, which could raise performance issues.
2. Passing in a filter: If you want to pass fields to filter on because you don't want all of
the data, for example in the case of a search query, make sure to use MongoDB $and
to intersect the filter coming from the client with the documents that client should be
allowed to see. Also, you should whitelist the keys that the client can use to filter - if the
client can filter on secret data, it can run a search to find out what that data is.
3. Passing in fields: If you want the client to be able to decide which fields of the
collection should be fetched, make sure to intersect that with the fields that client is
allowed to see, so that you don't accidentally send secret data to the client.

In summary, you should make sure that any options passed from the client to a publication
can only restrict the data being requested, rather than extending it.

Served files
Publications are not the only place the client gets data from the server. The set of source
code files and static assets that are served by your application server could also potentially
contain sensitive data:

1. Business logic an attacker could analyze to find weak points.


2. Secret algorithms that a competitor could steal.
3. Secret API keys.

Secret server code


While the client-side code of your application is necessarily accessible by the browser, every
application will have some secret code on the server that you don't want to share with the
world.

Secret business logic in your app should be located in code that is only loaded on the server.
This means it is in a server/ directory of your app, in a package that is only included on the
server, or in a file inside a package that was loaded only on the server.

If you have a Meteor Method in your app that has secret business logic, you might want to
split the Method into two functions - the optimistic UI part that will run on the client, and the
secret part that runs on the server. Most of the time, putting the entire Method on the server

248
Security

doesn't result in the best user experience. Let's look at an example, where you have a secret
algorithm for calculating someone's MMR (ranking) in a game:

// In a server-only file
MMR = {
updateWithSecretAlgorithm(userId) {
// your secret code here
}
}

// In a file loaded on client and server


const Meteor.users.methods.updateMMR = new ValidatedMethod({
name: 'Meteor.users.methods.updateMMR',
validate: null,
run() {
if (this.isSimulation) {
// Simulation code for the client (optional)
} else {
MMR.updateWithSecretAlgorithm(this.userId);
}
}
});

Note that while the Method is defined on the client, the actual secret logic is only accessible
from the server. Keep in mind that code inside if (Meteor.isServer) blocks is still sent to
the client, it is just not executed. So don't put any secret code in there.

Secret API keys should never be stored in your source code at all, the next section will talk
about how to handle them.

Securing API keys


Every app will have some secret API keys or passwords:

1. Your database password.


2. API keys for external APIs.

These should never be stored as part of your app's source code in version control, because
developers might copy code around to unexpected places and forget that it contains secret
keys. You can keep your keys separately in Dropbox, LastPass, or another service, and then
reference them when you need to deploy the app.

249
Security

You can pass settings to your app through a settings file or an environment variable. Most of
your app settings should be in JSON files that you pass in when starting your app. You can
start your app with a settings file by passing the --settings flag:

# Pass development settings when running your app locally


meteor --settings development.json

# Pass production settings when deploying your app to Galaxy


meteor deploy myapp.com --settings production.json

Here's what a settings file with some API keys might look like:

{
"facebook": {
"appId": "12345",
"secret": "1234567"
}
}

In your app's JavaScript code, these settings can be accessed from the variable
Meteor.settings .

Read more about managing keys and settings in the Deployment article.

Settings on the client


In most normal situations, API keys from your settings file will only be used by the server,
and by default the data passed in through --settings is only available on the server.
However, if you put data under a special key called public , it will be available on the client.
You might want to do this if, for example, you need to make an API call from the client and
are OK with users knowing that key. Public settings will be available on the client under
Meteor.settings.public .

API keys for OAuth


For the accounts-facebook package to pick up these keys, you need to add them to the
service configuration collection in the database. Here's how you do that:

First, add the service-configuration package:

meteor add service-configuration

Then, upsert into the ServiceConfiguration collection:

250
Security

ServiceConfiguration.configurations.upsert({
service: "facebook"
}, {
$set: {
appId: Meteor.settings.facebook.appId,
loginStyle: "popup",
secret: Meteor.settings.facebook.secret
}
});

Now, accounts-facebook will be able to find that API key and Facebook login will work
properly.

SSL
This is a very short section, but it deserves its own place in the table of contents.

Every production Meteor app that handles user data should run with SSL.

For the uninitiated, this means all of your HTTP requests should go over HTTPS, and all
websocket data should be sent over WSS.

Yes, Meteor does hash your password or login token on the client before sending it over the
wire, but that only prevents an attacker from figuring out your password - it doesn't prevent
them from logging in as you, since they could just send the hashed password to the server
to log in! No matter how you slice it, logging in requires the client to send sensitive data to
the server, and the only way to secure that transfer is by using SSL. Note that the same
issue is present when using cookies for authentication in a normal HTTP web application, so
any app that needs to reliably identify users should be running on SSL.

You can ensure that any unsecured connection to your app redirects to a secure connection
by adding the force-ssl package.

Setting up SSL
1. On Galaxy, most things are set up for you, but you need to add a certificate. See the
help article about SSL on Galaxy.
2. If you are running on your own infrastructure, there are a few options for setting up SSL,
mostly through configuring a proxy web server. See the articles: Josh Owens on SSL
and Meteor, SSL on Meteorpedia, and Digital Ocean tutorial with an Nginx config.

Security checklist

251
Security

This is a collection of points to check about your app that might catch common errors.
However, it's not an exhaustive list yet---if we missed something, please let us know or file a
pull request!

1. Make sure your app doesn't have the insecure or autopublish packages.
2. Validate all Method and publication arguments, and include the audit-argument-checks
to check this automatically.
3. Deny writes to the profile field on user documents.
4. Use Methods instead of client-side insert/update/remove and allow/deny.
5. Use specific selectors and filter fields in publications.
6. Don't use raw HTML inclusion in Blaze unless you really know what you are doing.
7. Make sure secret API keys and passwords aren't in your source code.
8. Secure the data, not the UI - redirecting away from a client-side route does nothing for
security, it's just a nice UX feature.
9. Don't ever trust user IDs passed from the client. Use this.userId inside Methods and
publications.
10. Set up browser policy, but know that not all browsers support it so it just provides an
extra layer of security to users with modern browsers.

252
Deployment and Monitoring

After reading this guide, you'll know:

1. What to consider before you deploy a Meteor application.


2. How to deploy to some common Meteor hosting environments.
3. How to design a deployment process to make sure your application's quality is
maintained.
4. How to monitor user behavior with analytics tools.
5. How to monitor your application with Kadira.
6. How to make sure your site is discoverable by search engines.

Deploying Meteor Applications


Once you've built and tested your Meteor application, you need to put it online to show it to
the world. Deploying a Meteor application is similar to deploying any other websocket-based
Node.js app, but is different in some of the specifics.

Deploying a web application is fundamentally different to releasing most other kinds of


software, in that you can deploy as often as you'd like to. You don't need to wait for users to
do something to get the new version of your software because the server will push it right at
them.

However, it's still important to test your changes throughly with a good process of Quality
Assurance (QA). Although it's easy to push out fixes to bugs, those bugs can still cause
major problems to users and even potentially data corruption!

Never use `--production` flag to deploy!


--production flag is purely meant to simulate production minification, but does almost

nothing else. This still watches source code files, exchanges data with package server
and does a lot more than just running the app, leading to unnecessary computing
resource wasting and security issues. Please don't use --production flag to deploy!

Deployment environments
In web application deployment it's common to refer to three runtime environments:

1. Development. This refers to your machine where you develop new features and run
local tests.
2. Staging. An intermediate environment that is similar to production, but not visible to
users of the application. Can be used for testing and QA.
3. Production. The real deployment of your app that your customers are currently using.

253
Deployment and Monitoring

The idea of the staging environment is to provide a non-user-visible test environment that is
as close as possible to production in terms of infrastructure. It's common for issues to
appear with new code on the production infrastructure that just don't happen in a
development environment. A very simple example is issues that involve latency between the
client and server---connecting to a local development server with tiny latencies, you just may
never see such an issue.

For this reason, developers tend to try and get staging as close as possible to production.
This means that all the steps we outline below about production deployment, should, if
possible, also be followed for your staging server.

Environment variables and settings


There are two main ways to configure your application outside of the code of the app itself:

1. Environment variables. This is the set of ENV_VARS that are set on the running
process.
2. Settings. These are in a JSON object set via either the --settings Meteor command-
line flag or stringified into the METEOR_SETTINGS environment variable.

Settings should be used to set environment (i.e. staging vs production) specific things, like
the access token and secret used to connect to Google. These settings will not change
between any given process running your application in the given environment.

Environment variables are used to set process specific things, which could conceivably
change for different instances of your application's processes. For instance, you can set a
different KADIRA_OPTIONS_HOSTNAME for each process to ensure that kadira logs timings with
useful hostnames.

A final note on storing these settings: It's not a good idea to store settings the same
repository where you keep your app code. Read about good places to put your settings in
the Security article.

Other considerations
There are some other considerations that you should make before you deploy your
application to a production host. Remember that you should if possible do these steps for
both your production and staging environments.

Domain name

254
Deployment and Monitoring

What URL will users use to access your site? You'll probably need to register a domain
name with a domain registrar, and setup DNS entries to point to the site (this will depend on
how you deploy, see below). If you deploy to Galaxy, you can use a x.meteorapp.com or
x.eu.meteorapp.com domain while you are testing the app. Learn more about Galaxy

domains »

SSL Certificate
It's always a good idea to use SSL for Meteor applications (see the Security Article to find
out why). Once you have a registered domain name, you'll need to generate an SSL
certificate with a certificate authority for your domain. If you deploy to Galaxy, you can
generate a free SSL certificate with a single click(courtesy of Let's Encrypt!).

CDN
It's not strictly required, but often a good idea to set up a Content Delivery Network (CDN)
for your site. A CDN is a network of servers that hosts the static assets of your site (such as
JavaScript, CSS, and images) in numerous locations around the world and uses the server
closest to your user to provide those files in order to speed up their delivery. For example, if
the actual web server for your application is on the east coast of the USA and your user is in
Australia, a CDN could host a copy of the JavaScript of the site within Australia or even in
the city the user is in. This has huge benefits for the initial loading time of your site.

The basic way to use a CDN is to upload your files to the CDN and change your URLs to
point at the CDN (for instance if your Meteor app is at http://myapp.com , changing your
image URL from <img src="http://myapp.com/cats.gif"> to <img
src="http://mycdn.com/cats.gif"> ). However, this would be hard to do with Meteor, since the

largest file – your Javascript bundle – changes every time you edit your app.

For Meteor, we recommend using a CDN with "origin" support (like CloudFront), which
means that instead of uploading your files in advance, the CDN automatically fetches them
from your server. You put your files in public/ (in this case public/cats.gif ), and when
your Australian user asks the CDN for http://mycdn.com/cats.gif , the CDN, behind the
scenes, fetches http://myapp.com/cats.gif and then delivers it to the user. While this is
slightly slower than getting http://myapp.com/cats.gif directly, it only happens one time,
because the CDN saves the file, and all subsequent Australians who ask for the file get it
quickly.

To get Meteor to use the CDN for your Javascript and CSS bundles, call
WebAppInternals.setBundledJsCssPrefix("http://mycdn.com") on the server. This will also

take care of relative image URLs inside your CSS files. If you need to use a dynamic prefix,

255
Deployment and Monitoring

you can return the prefix from a function passed to


WebAppInternals.setBundledJsCssUrlRewriteHook() .

For all your files in public/ , change their URLs to point at the CDN. You can use a helper
like assetUrl .

Before:

<img src="http://myapp.com/cats.gif">

After:

Template.registerHelper("assetUrl", (asset) => {


return "http://mycdn.com/" + asset
});

<img src="{{assetUrl 'cats.gif'}}">

CDNs and webfonts


If you are hosting a webfont as part of your application and serving it via a CDN, you may
need to configure the served headers for the font to allow cross-origin resource sharing (as
the webfont is now served from a different origin to your site itself). You can do this easily
enough in Meteor by adding a handler (you'll need to ensure your CDN is passing the
header through):

import { WebApp } from 'meteor/webapp';

WebApp.rawConnectHandlers.use(function(req, res, next) {


if (req._parsedUrl.pathname.match(/\.(ttf|ttc|otf|eot|woff|woff2|font\.css|css)$/))
{
res.setHeader('Access-Control-Allow-Origin', /* your hostname, or just '*' */);
}
next();
});

And then for example with Cloudfront, you would:

Select your distribution


Behavior tab
Select your app origin
Edit button
Under "Whitelist Headers", scroll down to select "Origin"

256
Deployment and Monitoring

Add button
"Yes, Edit" button

Deployment options
Meteor is an open source platform, and you can run the apps that you make with Meteor
anywhere just like regular Node.js applications. But operating Meteor apps correctly, so that
your apps work for everyone, can be tricky if you are managing your infrastructure manually.
This is why we recommend running production Meteor apps on Galaxy.

Galaxy (recommended)
The easiest way to operate your app with confidence is to use Galaxy, the service built by
Meteor Development Group specifically to run Meteor apps.

Galaxy is a distributed system that runs on Amazon AWS. If you understand what it takes to
run Meteor apps correctly and just how Galaxy works, you’ll come to appreciate Galaxy’s
value, and that it will save you a lot of time and trouble. Most large Meteor apps run on
Galaxy today, and many of them have switched from custom solutions they used prior to
Galaxy’s launch.

In order to deploy to Galaxy, you'll need to sign up for an account, and separately provision a
MongoDB database (see below).

Once you've done that, it's easy to deploy to Galaxy. You just need to add some
environment variables to your settings file to point it at your MongoDB, and you can deploy
with:

DEPLOY_HOSTNAME=us-east-1.galaxy-deploy.meteor.com meteor deploy your-app.com --settin


gs production-settings.json

In order for Galaxy to work with your custom domain ( your-app.com in this case), you need
to set up your DNS to point at Galaxy. Once you've done this, you should be able to reach
your site from a browser.

You can also log into the Galaxy UI at https://galaxy.meteor.com. Once there you can
manage your applications, monitor the number of connections and resource usage, view
logs, and change settings.

257
Deployment and Monitoring

If you are following our advice, you'll probably want to set up SSL on your Galaxy application
with the certificate and key for your domain. The key things here are to add the force-ssl
package and to use the Galaxy UI to add your SSL certificate.

Once you are setup with Galaxy, deployment is simple (just re-run the meteor deploy
command above), and scaling is even easier---simply log into galaxy.meteor.com, and scale
instantly from there.

258
Deployment and Monitoring

MongoDB hosting services to use with Galaxy


If you are using Galaxy (or need a production quality, managed MongoDB for one of the
other options listed here), it's usually a good idea to use a MongoDB hosting provider. There
are a variety of options out there, but a good choice is mLab. The main things to look for are
support for oplog tailing, and a presence in the us-east-1 or eu-west-1 AWS region.

Meteor Up
Meteor Up, often referred to as "mupx" or "mup", is a third-party open-source tool that can
be used to deploy Meteor application to any online server over SSH. It handles some of the
essential deployment requirements, but you will still need to do a lot of work to get your load
balancing and version updates working smoothly. It's essentially a way to automate the
manual steps of using meteor build and putting that bundle on your server.

You can obtain a server running Ubuntu or Debian from many generic hosting providers and
Meteor Up can SSH into your server with the keys you provide in the config. You can also
watch this video for a more complete walkthrough on how to do it.

Meteor Up has multiple projects so select what is best for your project:

259
Deployment and Monitoring

Original Meteor Up (not generally recommended any longer)


The mupx branch (best for pre-Meteor 1.4)
The kadirahq/mup fork (best for Meteor 1.4 or higher)

Currently, using Meteor Up with Meteor 1.4 requires kadirahq/mup (still in


development) and a special docker image with the correct Node version.

For further assistance, consult the documentation for the option you select.

Custom deployment
If you want to figure out your hosting solution completely from scratch, the Meteor tool has a
command meteor build that creates a deployment bundle that contains a plain Node.js
application. Any npm dependencies must be installed before issuing the meteor build
command to be included in the bundle. You can host this application wherever you like and
there are many options in terms of how you set it up and configure it.

NOTE it's important that you build your bundle for the correct architecture. If you are building
on your development machine, there's a good chance you are deploying to a different server
architecture. You'll want to specify the correct architecture with --architecture :

# for example if deploying to a Ubuntu linux server:


npm install --production
meteor build /path/to/build --architecture os.linux.x86_64

This will provide you with a bundled application .tar.gz which you can extract and run
without the meteor tool. The environment you choose will need the correct version of
Node.js and connectivity to a MongoDB server.

Depending on the version of Meteor you are using, you should install the proper version of
node using the appropriate installation process for your platform.

Node 4.4.7 for Meteor 1.4.x


Node 0.10.43 for Meteor 1.3.x and earlier

If you use a mis-matched version of Node when deploying your application, you will
encounter errors!

You can then run the application by invoking node with a ROOT_URL , and MONGO_URL . These
instructions are also available in the README file found in the root of the bundle you built
above.

260
Deployment and Monitoring

cd my_build_bundle_directory
(cd programs/server && npm install)
MONGO_URL=mongodb://localhost:27017/myapp ROOT_URL=http://my-app.com node main.js

ROOT_URL is the base URL for your Meteor project

MONGO_URL is a Mongo connection string URI supplied by the MongoDB provider.

Unless you have a specific need to roll your own hosting environment, the other options here
are definitely easier, and probably make for a better setup than doing everything from
scratch. Operating a Meteor app in a way that it works correctly for everyone can be
complex, and Galaxy handles a lot of the specifics like routing clients to the right containers
and handling coordinated version updates for you.

Deployment process
Although it's much easier to deploy a web application than release most other types of
software, that doesn't mean you should be cavalier with your deployment. It's important to
properly QA and test your releases before you push them live, to ensure that users don't
have a bad experience, or even worse, data get corrupted.

It's a good idea to have a release process that you follow in releasing your application.
Typically that process looks something like:

1. Deploy the new version of the application to your staging server.


2. QA the application on the staging server.
3. Fix any bugs found in step 2. and repeat.
4. Once you are satisfied with the staging release, release the exact same version to
production.
5. Run final QA on production.

Steps 2. and 5. can be quite time-consuming, especially if you are aiming to maintain a high
level of quality in your application. That's why it's a great idea to develop a suite of
acceptance tests (see our Testing Article for more on this). To take things even further, you
could run a load/stress test against your staging server on every release.

Continuous deployment
Continuous deployment refers to the process of deploying an application via a continuous
integration tool, usually when some condition is reached (such as a git push to the master
branch). You can use CD to deploy to Galaxy, as Nate Strauser explains in a blog post on
the subject.

261
Deployment and Monitoring

Rolling deployments and data versions


It's important to understand what happens during a deployment, especially if your
deployment involves changes in data format (and potentially data migrations, see the
Collections Article).

When you are running your app on multiple servers or containers, it's not a good idea to shut
down all of the servers at once and then start them all back up again. This will result in more
downtime than necessary, and will cause a huge spike in CPU usage when all of your clients
reconnect again at the same time. To alleviate this, Galaxy stops and re-starts containers
one by one during deployment. There will be a time period during which some containers are
running the old version and some the new version, as users are migrated incrementally to
the new version of your app.

If the new version involves different data formats in the database, then you need to be a little
more careful about how you step through versions to ensure that all the versions that are
running simultaneously can work together. You can read more about how to do this in the
collections article.

262
Deployment and Monitoring

Monitoring users via analytics


It's common to want to know which pages of your app are most commonly visited, and
where users are coming from. Here's a simple setup that will get you URL tracking using
Google Analytics. We'll be using the okgrow:analytics package.

meteor add okgrow:analytics

Now, we need to configure the package with our Google Analytics key (the package also
supports a large variety of other providers, check out the documentation on Atmosphere).
Pass it in as part of Meteor settings:

{
"public": {
"analyticsSettings": {
// Add your analytics tracking id's here
"Google Analytics" : {"trackingId": "Your tracking ID"}
}
}
}

The analytics package hooks into Flow Router (see the routing article for more) and records
all of the page events for you.

You may want to track non-page change related events (for instance publication
subscription, or method calls) also. To do so you can use the custom event tracking
functionality:

export const updateText = new ValidatedMethod({


...
run({ todoId, newText }) {
// We use `isClient` here because we only want to track
// attempted method calls from the client, not server to
// server method calls
if (Meteor.isClient) {
analytics.track('todos.updateText', { todoId, newText });
}

// ...
}
});

To achieve a similar abstraction for subscriptions/publications, you may want to write a


simple wrapper for Meteor.subscribe() .

263
Deployment and Monitoring

Monitoring your application


When you are running an app in production, it's vitally important that you keep tabs on the
performance of your application and ensure it is running smoothly.

Understanding Meteor performance


Although a host of tools exist to monitor the performance of HTTP, request-response based
applications, the insights they give aren't necessarily useful for a connected client system
like a Meteor application. Although it's true that slow HTTP response times would be a
problem for your app, and so using a tool like Pingdom can serve a purpose, there are many
kinds of issues with your app that won't be surfaced by such tools.

Monitoring with Galaxy


Galaxy offers turnkey Meteor hosting and provides tools that are useful to debug the current
and past state of your application. CPU and Memory load graphs in combination with
connected user counts can be vital to determining if your setup is handling the current load
(or if you need more containers), or if there's some specific user action that's causing
disproportionate load (if they don't seem to be correlated):

264
Deployment and Monitoring

Galaxy's UI provides a detailed logging system, which can be invaluable to determine which
action it is causing that extra load, or to generally debug other application issues:

265
Deployment and Monitoring

Kadira
If you really want to understand the ins and outs of running your Meteor application, you
should give Kadira a try. Kadira is a full featured Application Performance Monitoring (APM)
solution that's built from the ground up for Meteor. Kadira operates by taking regular client
and server side observations of your application's performance as it conducts various
activities and reporting them back to a master server.

When you visit the Kadira application, you can view current and past behavior of your
application over various useful metrics. Kadira's documentation is extensive and invaluable,
but we'll discuss a few key areas here.

Method and Publication Latency


Rather than monitoring HTTP response times, in a Meteor app it makes far more sense to
consider DDP response times. The two actions your client will wait for in terms of DDP are
method calls and publication subscriptions. Kadira includes tools to help you discover which
of your methods and publications are slow and resource intensive.

266
Deployment and Monitoring

In the above screenshot you can see the response time breakdown of the various methods
commonly called by the Atmosphere application. The median time of 56ms and 99th
percentile time of 200ms seems pretty reasonable, and doesn't seem like too much of a
concern

You can also use the "traces" section to discover particular cases of the method call that are
particular slow:

267
Deployment and Monitoring

In the above screenshot we're looking at a slower example of a method call (which takes
214ms), which, when we drill in further we see is mostly taken up waiting on other actions on
the user's connection (principally waiting on the searches/top and counts publications).
So we could consider looking to speed up the initial time of those subscriptions as they are
slowing down searches a little in some cases.

Livequery Monitoring
A key performance characteristic of Meteor is driven by the behavior of livequery, the key
technology that allows your publications to push changing data automatically in realtime. In
order to achieve this, livequery needs to monitor your MongoDB instance for changes (by
tailing the oplog) and decide if a given change is relevant for the given publication.

If the publication is used by a lot of users, or there are a lot of changes to be compared, then
these livequery observers can do a lot of work. So it's immensely useful that Kadira can tell
you some statistics about your livequery usage:

268
Deployment and Monitoring

In this screenshot we can see that observers are fairly steadily created and destroyed, with a
pretty low amount of reuse over time, although in general they don't survive for all that long.
This would be consistent with the fact that we are looking at the package publication of
Atmosphere which is started everytime a user visits a particular package's page. The
behavior is more or less what we would expect so we probably wouldn't be too concerned by
this information.

Enabling SEO
If your application contains a lot of publicly accessible content, then you probably want it to
rank well in Google and other search engines' indexes. As most webcrawlers do not support
client-side rendering (or if they do, have spotty support for websockets), it's better to render
the site on the server and deliver it as HTML in this special case.

To do so, we can use the Prerender.io service, thanks to the dfischer:prerenderio


package. It's a simple as meteor add -ing it, and optionally setting your prerender token if
you have a premium prerender account and would like to enable more frequent cache
changes.

269
Deployment and Monitoring

If you’re using Galaxy to host your meteor apps, you can also take advantage of built-in
automatic Prerender.io integration. Simply add mdg:seo to your app and Galaxy will take
care of the rest.

Chances are you also want to set <title> tags and other <head> content to make your
site appear nicer in search results. The best way to do so is to use the kadira:dochead
package. The sensible place to call out to DocHead is from the onCreated callbacks of your
page-level components.

270

You might also like