# Meteor Tutorial: First steps

Application structure

The Application structure roughly follows the guidelines outlined in the official Meteor documentation on Application Structure. This is what your meteor-todolist folder looks like:


.
├── .bowerrc
├── .gitignore
├── .meteor
├── LICENSE.txt
├── bower.json
├── client
├── imports
├── package.json
├── packages
├── public
├── server
└── webpack.json

When you first launched the meteor server and navigated to http://localhost:3000 you were greeted by a "Hello, World!" page. This page is defined in imports/routes/Home.js.

The routes for all pages are defined in imports/routes.js. Sidebar navigation items are defined in imports/common/sidebar.js. Similarly, the header is defined in imports/common/header.js and the footer in imports/common/footer.js.

The main layout file that loads the scripts/styles is located in client/main.html. The main entry file for the client is client/main.js. This file also loads the main stylesheet for our app. The stylesheet is a SASS file and is located in client/sass/main.scss.

All your third-party Javascript files should be defined in imports/plugins.js. At this point you might be wondering why it is not possible to directly define script tags in your main.html. The reason being that, in production, Meteor moves the script tags you defined in the header to the bottom of the body tag below your app's script tag, causing the code to break. So we have written a simple plugin that inserts the script tags in the header before the final HTML is rendered (on both client and server).


API Setup

Create a file imports/api/todos.js which will house our Todo collection, a seeder to seed the database with dummy data along with Meteor publications and Meteor methods. Paste the following code into the newly created file:


/**
 * File: imports/api/todos.js
 */

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

export const Todos = new Mongo.Collection('todos');

require('./seeder')(Todos);

Meteor.methods({
  'todos.setChecked'(_id, completed) {
    check(completed, Boolean);

    Todos.update({ _id },
                 { $set: { completed } });
  },
  'todos.remove'(_id) {
    Todos.remove({ _id });
  },
  'todos.create'(todo) {
    check(todo, String);

    if (todo.length <= 0) {
      throw new Meteor.Error(403, `'todo' should not be empty!`);
      return;
    }

    Todos.insert({ todo, completed: false });
  },
  'todos.update'(_id, todo, completed) {
    check(todo, String);
    check(completed, Boolean);

    Todos.update({ _id },
                 { $set: { todo, completed } });
  }
});

if (Meteor.isServer) {
  // This code only runs on the server
  Meteor.publish('todos', () => {
    return Todos.find();
  });

  Meteor.publish('todo', (_id) => {
    return Todos.find({ _id });
  });
}

Let's examine the file in detail:

  • In Line 9, we create a new todos collection in our Mongo database.
  • We then import a seeder which loads our database with dummy data (Line 11). The seeder file will be defined in the next section.
  • Line 13-40 defines all the Meteor methods that we will be calling from our React components. Please note that this todo list example does not include "users/roles" to keep it simple and easy to grok.
  • Line 42-51 defines Meteor publications that will be called only on the server.

Database seeder

In this section, we shall define a database seeder. Create a file imports/api/seeder.js and paste the following code into the newly created file:


/**
 * File: imports/api/seeder.js
 */

import { Meteor } from 'meteor/meteor';

export default function(Todos) {
  if (Meteor.isServer) {
    if (process.env.NODE_ENV !== 'production') {
      const todos = Todos.find({}).fetch();
      const data = [
        'Prepare new Billing format',
        'Create benefits presentation',
        'Prepare productivity report',
        'Review non-exempt evaluations',
        'Update insurance information',
      ];

      if (!todos.length) {
        for (let i = 0; i < data.length; i++) {
          Todos.insert({
            todo: data[i],
            completed: false
          });
        }
      }
    }
  }
}

The above code is pretty self-explanatory. We create a bunch of todos if and only if the Todos collection is empty. We also seed the database only when in development mode.