E42: Experience Meteor Mongo

Now that you have some basic comfort with creating collections and documents in Mongo, it’s time to take a closer look at how Mongo is integrated with Meteor.

Preview

This is not a practice WOD because I could not find any Readings to introduce this material. So, please watch me do it first before trying it yourself:

Task 1: Initial setup

Do the following to set yourself up for development:

  1. Create a GitHub repo called “meteor-mongo-practice”.

  2. Clone it to your local file space.

  3. cd into your local meteor-mongo-practice directory.

  4. Use meteor create meteor-mongo-practice to create an app called “meteor-mongo-practice” inside your repo.

  5. Change the name of the meteor-mongo-practice subdirectory to app/.

  6. cd into the app/ directory, and invoke meteor run to run the app. Go to http://localhost:3000 to verify that the default Meteor app is running.

  7. Create an IntelliJ project called “meteor-mongo-practice” that points to your repo.

  8. If IntelliJ tries to run ESLint and fails, click on “Settings” in the failure dialog and uncheck the “Enable” box to disable ESLint. (Don’t worry: We’ll configure ESLint next week.)

Task 2: Install Meteor Dev Tools

There is a Chrome Developer Tools extension called “Meteor Dev Tools” that (among other things) makes it much easier to inspect the contents of the local Mini-Mongo database that runs in your Meteor clients.

Go to the Meteor Dev Tools Installation Page to install it.

Task 3: Create the People collection and a sample document.

For this step, we will recreate the People collection and sample documents from the last experience. However, this time we will do it using Meteor rather than via the MongoDB shell.

Create the both/ directory

First, inside the app/ directory, create a directory called both/. This results in three application directories:

Create the both/PeopleCollection.js file

Inside the app/both/ directory, create a file called PeopleCollection.js. Inside this file, put the following code:

import { Mongo } from 'meteor/mongo';

export const people = new Mongo.Collection('People');

The import statement says to make the predefined “Mongo” class available to code inside this file.

The export statement creates a new Mongo collection called “People” and bind it to the variable “people”. Finally, the statement exports the people variable, which means other files can access the people variable and its value by importing it.

Because the PeopleCollection.js file is in a directory named “both”, this code will be loaded in both the client and the server, which means that both client and server code can access the People collection by manipulating the variable named people.

For more details on the Mongo.Collection function, see the Mongo.Collection documentation.

Import the PeopleCollection in the client

Edit the client/main.js file adding the line:

import { people } from '../both/PeopleCollection';

This makes the PeopleCollection available on the client.

Create the server/PeopleInitialization.js file

Inside the app/server/ directory, create a file called PeopleInitialization.js. Inside this file, put the following code:

import { people } from '../both/PeopleCollection.js';

people.insert({ first: 'Philip', last: 'Johnson', age: 60, city: 'Kailua'});
people.insert({ first: 'Joanne', last: 'Amberg', age: 58, city: 'Kailua' });
people.insert({ first: 'Jenna', last: 'Corin', age: 31, city: 'Boulder Creek' });
people.insert({ first: 'Katie', last: 'Kai', age: 25, city: 'Palo Alto' });

The import statement says to load the PeopleCollection.js file (if it hasn’t already been loaded), and make the “people” variable defined within that file available for use in this file.

The following lines insert four documents into the MongoDB “People” collection.

For more details on the insert function, see the Mongo.Collection insert documentation.

Import the PeopleIntialization in the server

Edit server/main.js to include the line:

import './PeopleInitialization';

This ensures the PeopleCollection is initialized at startup.

Inspect the server-side and client-side databases

Once you’ve defined these files, let’s look at the server-side and client-side MongoDB databases.

To look at the server-side MongoDB database, open up a new shell, cd into the meteor-mongo-practice/app directory, and type meteor mongo. Once the shell comes up, type show collections and then db.People.find(). Here’s what should result:

To look at the client-side MongoDB database, open up Chrome Developer tools in the window that is showing http://localhost:3000, and click to the “Meteor” tab, then click on “Mini Mongo”, then click “People”. Here’s what you should see:

Task 4: Experience the effect of dynamic reloading in Meteor

Let’s say you want to add a new document to our system. Copy the following line and add it to the end of the file in PeopleInitialization:

people.insert({ first: 'Doris', last: 'Johnson', age: 93, city: 'Naples' });

Now press command-S (on Mac) to save the file. In the Meteor console, you should see the system restarting, with a message “Meteor server restarted”.

Now if you look in either the client or server Mongo databases, you’ll see that there are 9 documents, not five!

This is because we reloaded the PeopleInitialization.js file, and when we did that, we defined five new documents on top of the four that already existed. If you inspect the documents in Chrome Developer Tools, you’ll see that there are two copies of documents for Philip, Joanne, Jenna, and Katie, plus a single new one for Doris.

Dynamic reloading is a great help during development, since you can make changes to your code and see the results quickly. However, this is certainly not the behavior we want!

Task 5: Create “initialization” code and reset the database

One way to solve this problem is to specify that these “initial” inserts should be done only when the collection is empty. We can do that by changing the contents of the PeopleInitialization file to this:

import { people } from '../both/PeopleCollection.js';

if (people.find().count() === 0) {
  people.insert({ first: 'Philip', last: 'Johnson', age: 60, city: 'Kailua' });
  people.insert({ first: 'Joanne', last: 'Amberg', age: 58, city: 'Kailua' });
  people.insert({ first: 'Jenna', last: 'Corin', age: 31, city: 'Boulder Creek' });
  people.insert({ first: 'Katie', last: 'Kai', age: 25, city: 'Palo Alto' });
  people.insert({ first: 'Doris', last: 'Johnson', age: 93, city: 'Naples' });
}

As you can see, we now check that the people collection is empty before doing any inserts. (For more details, see the find() and count()) documentation.

To see if things work correctly, we first have to reset our application’s database. To do that, control-c to stop the running application, then invoke meteor reset. The reset command re-initializes the data. For more information, see the reset command documentation.

Now if you run meteor run, you should find that the database has just five elements in it, and that if you add a space to a file and save (forcing a restart), the number of documents does not change.

Meteor bug alert!

Warning! There is a bug in Meteor (Issue 9026) which results in a "Mongo Error: Not master" being printed to the console followed by a successful restart. This is due to a race condition that has been fixed and will appear in a future update of Meteor. Simply ignore this error, allow the system to restart, and everything will be fine.

Task 6: Commit your code to GitHub

When you have finished this exercise, commit your code to GitHub. This is the way we can check that you carried out the instructions in this assignment. It also provides sample code for you to look at in future to remind yourself of how Meteor and Mongo work together.

Summary

If you’ve followed along, you should now have a basic idea of how Meteor and Mongo work together. In brief:

Submission instructions

By the time and date indicated on the Schedule page, submit this assignment via Laulima. To submit, please provide the URL to your GitHub repository.

This is not a practice WOD, so you do not have to time yourself.