First steps with Meteor Framework

This is the basic structure for a meteor App:

client/main.js        # a JavaScript entry point loaded on the client
client/main.html      # an HTML file that defines view templates
client/main.css       # a CSS file to define your app's styles
server/main.js        # a JavaScript entry point loaded on the server
test/main.js          # a JavaScript entry point when running tests
.meteor/              # internal Meteor files

You can manage the server and client code from the same tool. This is an example for a server code with 3 methods: insert, remove and setChecked to put some task as done.

import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';

export const Tasks = new Mongo.Collection('tasks');

    'tasks.insert'(text) {
        check(text, String);

        // Make sure the user is logged in before inserting a task
        if (!this.userId) {
            throw new Meteor.Error('not-authorized');

            createdAt: new Date(),
            owner: this.userId,
            username: Meteor.users.findOne(this.userId).username,

    'tasks.remove'(taskId) {
        check(taskId, String);


    'tasks.setChecked'(taskId, setChecked) {
        check(taskId, String);
        check(setChecked, Boolean);

        Tasks.update(taskId, { $set: { checked: setChecked } });

Meteor.publish and Meteor.subscribe

  • meteor remove autopublish all new Meteor apps start with the autopublish package if users of our application want to store privacy-sensitive data this is not secure.
  • We need a way of controlling which data Meteor sends to the client-side database.
  • Without the autopublish package, we will have to specify explicitly what the server sends to the client

Code in the server imports/ui/App.js

if (Meteor.isServer) {
    // This code only runs on the server
    Meteor.publish('tasks', function tasksPublication() {
        return Tasks.find({
            $or: [
                { private: { $ne: true } }, 
                { owner: this.userId }, // only send to the client task which is owner

Code in client component imports/ui/Component.js

export default withTracker(() => {
  // subscribes to data from that publication
  // tasks that the user is the owner
  return {
    tasks: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),
    // more code...

Templating Engine

  • Meteor use BlazeJS as a templating engine.
  • To prevent processing .html files as Blaze templates: meteor remove blaze-html-templates and meteor add static-html
  • blaze-html-templates and static-html are Meteor packages and not npm packages
  • Compilation is one of several key features that make Meteor packages more powerful than npm packages

accounts-ui package

  • meteor add accounts-ui accounts-password meteor comes with an accounts system and a drop-in login user interface that lets you add multi-user functionality to your app in minutes. (Login only with Blaze). You have a lot of features included in this package. For example Facebook or Google account Login docs accounts-ui
  • In your data container, you can use Meteor.user() to check if a user is logged in and get information about them
  • Example of Methods using the accounts-ui package and also adding security with methods
import React, { useState } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';

import { Tasks } from '../api/tasks.js';
import Task from './Task';

// To use the Blaze UI component from the accounts-ui package
import AccountsUIWrapper from './AccountsUIWrapper';

// App component - represents the whole app
const App = ({ incompleteCount, tasks, currentUser }) => {
  const [text, setText] = useState('');
  const [hideCompleted, setHideCompleted] = useState(false);

  const renderTasks = () => {
    const currentUserId = currentUser && currentUser._id;
    return tasks.filter(task => hideCompleted ? !task.checked : true).map((task) => (
      <Task showPrivateButton={task.owner === currentUserId} key={task._id} task={task} />

  const handleSubmit = (event) => {
    event.preventDefault();'tasks.insert', text);

  return <>
    <div className="container">
        <h1>Todo List ({incompleteCount})</h1>

        <label className="hide-completed">
            onClick={() => setHideCompleted((prev) => !prev)}
            Hide Completed Tasks

        <AccountsUIWrapper />

        {currentUser && <form className="new-task" onSubmit={handleSubmit} >
            placeholder="Type to add new tasks"
            onChange={e => setText(}


// the component is subscribed or receiving data from the database - withTracker
// we can suscribe to the currentUser with Meteor.user()
export default withTracker(() => {
  return {
    tasks: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),
    incompleteCount: Tasks.find({ checked: { $ne: true } }).count(),
    currentUser: Meteor.user()

Configure the accounts UI to use usernames instead of email addresses in imports/startup/accounts-config.js

import { Accounts } from 'meteor/accounts-base';

  passwordSignupFields: 'USERNAME_ONLY'

Testing in meteor

Add the dependencies:

meteor add meteortesting:mocha meteor npm install --save-dev chai

  • tests/main.js entry point for all your application tests
  • You can split your tests across multiple modules using the convention {filenameToTest}.test.js

Simple test file example:

import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { assert } from 'chai';

import { Tasks } from './tasks.js';

if (Meteor.isServer) {
    describe('Tasks', () => {
        describe('methods', () => {
            const userId =;
            let taskId;
            beforeEach(() => {
                taskId = Tasks.insert({
                    text: 'test task',
                    createdAt: new Date(),
                    owner: userId,
                    username: 'tmeasday',

            it('can delete owned task', () => {
                // Find the internal implementation of the task method so we can
                // test it in isolation
                const deleteTask = Meteor.server.method_handlers['tasks.remove'];

                // Set up a fake method invocation that looks like what the method expects
                const invocation = { userId };

                // Run the method with `this` set to the fake invocation
                deleteTask.apply(invocation, [taskId]);

                // Verify that the method does what we expected
                assert.equal(Tasks.find().count(), 0);

Keypoints in Meteor

  • The entry point for both client and server JavaScript is determined by the meteor.mainModule section in package.json
  • imports/api This is a sensible place to store API-related files for the application.
  • methods write to collections
  • publications read from collections
  • meteor add react-meteor-data install this meteor package to use data from a Meteor collection inside a React component
  • meteor mongo access to the meteor mongo console
  • meteor npm install -g yarn install yarn inside the meteor system to use it
  • meteor add nathantreid:static-assets meteor package to use .svg files inside our project