≡ Menu

Getting Started with Databases in Meteor: A Complete Beginner’s Guide to MongoDB

mongdodb-tutorial

If you’re looking to make web applications, you’ll have to mess around with databases sooner or later. They’re how we store data persistently and are therefore needed if we’re going to:

  • Have an “accounts” system where users can register and login.
  • Allow users to save preferences that are remembered between visits.
  • Publish content (comments, etc) that can be seen by other users.

Luckily enough, Meteor comes bundled with MongoDB and, since there’s no configuration required or difficult syntax to learn, working with databases is surprisingly simple. Within the next few thousand words, you’ll learn everything you need to know to get started.

Let’s begin.

Introducing, MongoDB

There’s a range of database types available for use with web applications — MySQL is what most people are familiar with. At the moment, MongoDB is the only one available to use with Meteor, which is limiting to some, but it’s great for beginner developers since it’s:

  • Fast.
  • Scalable.
  • Simple.

It has its critics, of course, but I’d say it’s easier to learn than something like MySQL, so even if you have limited experience with databases, it’s not hard to grasp.

Since MongoDB is independent of Meteor though — they are not inextricably linked — it’s worth learning about MongoDB independently. This tutorial will cover the basics you need to know in the context of Meteor but, eventually, it’s worth checking out:

You don’t have to consult these sources now but, as you get deeper into Meteor, a deeper understanding of the technologies behind Meteor will make you a better developer.

MongoDB vs. MySQL

I’d love for this tutorial to introduce beginning web developers to databases in general but my attempts to do so caused more bloat than I cared to publish. Therefore, I will assume at least a basic familiarity with SQL concepts, such as:

  • Tables.
  • Rows.
  • Columns.

With this knowledge, MongoDB is easier to learn as these elements have direct counterparts:

SQL MongoDB

MongoDB is unique in many ways but, as far as a beginner is concerned, it’s mostly a matter of using different words to describe similar things, so just keep those words in mind.

Databases

When we create a new Meteor project, a database is created for us. We don’t need to take any further step since Meteor assumes you’ll want to use a database (and that is what we’ll want to use in most cases). As a result:

  • There’s no configuration required.
  • We can get to work straight away.

This is just one of the many features in Meteor that shows how optimised it is for developer happiness. You won’t be stuck doing much of the laborious junk as in other frameworks.

Collections

To insert data into a database (and then to manipulate it), we need to create at least one collection, and as shown above, a collection is equivalent to an SQL table.

In this tutorial, we’ll imagine we want to create a blogging application like WordPress and, therefore, we’ll create a “posts” collection to store our blog posts.

To do this, let’s start by creating a new Meteor project:

meteor create DatabaseExample

Then empty out the contents of the default JavaScript file and remove the contents between the <body> tags within the HTML file. We want a (relatively) fresh project to work with.

At the top of the JavaScript file, write this line:

new Meteor.Collection('posts');

This is how we create a collection named “posts” in our MongoDB database. You can call your collections whatever you want as long as the name is unique.

There is a problem with this code though: we have no way of referencing our collection and, therefore, we have no way of inserting, modifying, or removing its data. We have a collection but we’re unable to use that collection.

To remedy this, we place a variable in front of the previous statement:

OurPosts = new Meteor.Collection('posts');

Notice, however, that we don’t use the “var” keyword. That’s because we’re using a global variable so we can access our collection throughout our application and not just in a single file.

To make sure this collection actually exists, launch the local server:

meteor run

Then navigate to http://localhost:3000 in Google Chrome. As per usual, I recommend Chrome because of the JavaScript Console feature that comes in handy during Meteor development.

You won’t see anything within the user interface but open the JavaScript Console and enter the name of your collection’s variable:

OurPosts

The console will spit out some data confirming that the collection exists. If an error is returned, make sure the syntax in your JavaScript file matches this:

OurPosts = new Meteor.Collection('posts');

You can create multiple collections in the same way but we’ll just be working with one during this tutorial. Once the collection is confirmed to exist, we can move on.

Client vs. Server

You might have noticed that we didn’t place the code to create our collection within an isClient or isServer conditional. This means the code is executed on both the client and the server. This allows for something very interesting to happen:

When the code executes on the server, it works as expected. The collection is created and we’re able to manipulate the data within that collection. It’s like any normal database.

When the code execute on the client though, it sends a copy of the collection to the browser and, when we’re interacting with our web application, we’re actually coming into contact with this copy rather than the actual collection. The copy, however, is synced with the original.

This might sound needlessly abstract but, because we’re interacting with locally stored data when using a Meteor application, it feels incredibly responsive — almost like a desktop app.

If you’re wondering if it’s a security risk to send all of this data to the client, the answer is, “Yes, it is.” Meteor has you covered though and we’ll cover the basics of security in another tutorial.

Inserting Data

You can’t do much with an empty collection so let’s take a moment to insert some data. To do this, we can attach the insert() function to the OurPosts variable:

OurPosts.insert();

But alone, this does nothing. We’ll need to pass some actual data through the function, such as:

OurPosts.insert({
title: "My Blog Post Title",
body: "This is the body of the post.",
author: "David Turnbull"
});

We can enter this straight into the JavaScript Console and the data will be inserted into the collection we created earlier, but let’s break down exactly what’s happening:

  • We reference the “OurPosts” variable for our collection.
  • We attach the insert() function to our collection’s variable.
  • We pass key-value data through the insert() function.

In MongoDB, we’re creating what’s called a document — the equivalent to a row in SQL. This document contains a range of fields (key-value pairs) and, in a table, looks like this:

postscollection

Furthermore, here’s a couple of things to keep in mind:

  • You’re able to design your database structure on the fly. You don’t have to predefine any of the fields and, if you like, every document can be structured differently.
  • Fields can contain any sort of data. We’ve only used strings so far but feel free to use numbers or booleans or whatever. MongoDB won’t flinch at whatever you use.

To demonstrate these points, imagine that all of posts in our collection are considered drafts by default. To define a post as published, then, we could create an “isPublished” boolean:

OurPosts.insert({
title: "My Second Blog Post",
body: "Here's yet another post.",
author: "David Turnbull",
isPublished: true
});

By doing this, we’ll be able to retrieve posts based on whether or not they’re published. We didn’t, however, have to predefine the structure of our database to allow for the “isPublished” parameter or even state that, “This is a boolean value…” MongoDB just gets what’s going on.

Finding Data, Part 1

Once we’ve inserted data into a collection, it makes sense to get it out at some point. There’s two main ways to do this:

  • Through the JavaScript Console.
  • Through a user interface.

We’ll cover both options in this tutorial but let’s start with the console since that’s usually the most direct, simplest way to begin.

To retrieve data from a collection, we use the find() function:

OurPosts.find();

Run that in the console and the data within the collection should be spit back out. To make this data more readable though, attach the fetch() function to the statement:

OurPosts.find().fetch();

This converts the data from the find() function into an array but it’s only a convenience when working within the console. You won’t need to use fetch() within your application’s code.

At the moment, we’re retrieving all of the data from a collection. That can be useful at times but it’s a lot more useful to retrieve specific data. One way of doing this is by writing:

OurPosts.find({
isPublished: true
});

It’s the same format as inserting data except that, based on the parameters we pass through the find() function, certain data is retrieved. In this case, we’re retrieving all documents where the “isPublished” parameter is equal to true. This is how we retrieve our published posts.

Note: There are more advanced queries available but I’ll cover them in another tutorial since there’s a lot to talk about and I don’t want to drown you in details.

A variant of the find() function is the findOne() function. This works in the same way except that it only retrieves the first piece of data it finds:

OurPosts.findOne({
isPublished: true
});

An example of when this would be useful is if you were designing a profile page for a user and only needed to retrieved the data for an individual user. Using the findOne() function would be more efficient since it wouldn’t filter through all of the users.

Finding Data, Part 2

Now that we know the basics of retrieving data, we can apply that knowledge to having that data appear within a user interface. If you haven’t already though, you might want to read the “How To Make the Default Meteor Application” tutorial since that has better coverage of:

  • Templates.
  • Helpers.
  • Events.

But I’ll do what I can to keep this as simple as possible.

Within the JavaScript file, setup the isClient conditional:

if ( Meteor.isClient ){
// code goes here
}

The code we’re about to write is all about templates and we therefore don’t want it running on the server. (If we tried to make it run on the server, our Meteor app would crash.)

Then, within that conditional, we’ll define a “postsList” template, which we haven’t created yet, and attach an “items” helper to the end of it:

Template.postsList.items = function(){
// code goes here
};

In the HTML file, we’ll create this template beneath the closing <body> tag:

<template name="postsList">
</template>

Then include this template between the <body> tags:

{{> postsList }}

If you’ve done this correctly, the application won’t look any different but it should at least continue to function without any errors.

Back in the JavaScript file, change the helper so it looks like this:

Template.postsList.items = function(){
return OurPosts.find();
}

The code’s becoming a little busier but it’s nothing we haven’t worked with before. We have our helper and, within it, we’re retrieving all of the data from the “OurPosts” collection.

Switch back to the HTML file and, between the <template> tags, write:

{{#each items}}
{{title}}
{{/each}}

What we’re doing here is looping through the data that’s retrieved by the find() function and showing the “title” field from each document that’s found. The “items” part is the name of the helper we defined in the JavaScript file.

For something a little prettier, we could change the template code to:

<ul>
{{#each items}}
<li>{{title}}, by {{author}}</li>
{{/each}}
</ul>

This places the titles of our posts and the author’s name within a formatted list.

But what if we only wanted to display posts that are set as published? Then we just change the find() function within our helper:

Template.postsList.items = function(){
return OurPosts.find({ isPublished: true });
}

Now the template will only list the posts where the “isPublished” parameter is equal to true.

Counting Data

One of the simplest things to do with MongoDB that comes in handy on a regular basis is the ability to count the number of documents (or “entries”) in a particular collection. You might, for instance, want to count (and then display) how many blog posts have been published.

To demonstrate the basic syntax for counting documents, write this in the JavaScript Console:

OurPosts.find().count();

By attaching the count() function to the find() function, we’re able to return the total number of documents within the collection.

To count a precise portion of a collection, use the find() function with parameters:

OurPosts.find({
isPublished: true
}).count();

This will return all documents where the “isPublished” parameter is equal to true.

Let’s add this data to the template with the use of a helper in our JavaScript file:

Template.postsList.publishedCount = function(){
return OurPosts.find({ isPublished: true }).count();
}

Here, we’ve created a “publishedCount” helper and made it return the number of documents in our collection where “isPublished” equals true.

To show this data within the web page, add this to the top of the “postsList” template:

<p>You have published {{publishedCount}} posts.</p>

You might also want to create another helper and modify the template so it shows how many posts are not published.

Creating Forms

Earlier, we inserted some data into our database through the JavaScript Console, but it’s hardly reasonable to expect users to do the same. Therefore, let’s create a (very basic) interface.

In case you’re new to developing web applications, know that it’s worth heeding this advice from the folks at 37Signals:

Too many apps start with a program-first mentality. That’s a bad idea. Programming is the heaviest component of building an app, meaning it’s the most expensive and hardest to change. Instead, start by designing first.

So while this project is going to be simple enough that it doesn’t really matter if you start with the design or the programming logic, the design-first mentality is useful for the future.

To begin, create a template that will hold a form:

<template name="newPostForm">
</template>

We don’t have to create this new template but, the more modular our code is, the easier time we’ll have navigating through it in the future.

From there, include this template anywhere you like using the partial syntax:

{{> newPostForm }}

You could include this between the <body> tags or within our postsList template.

Next, create the form itself in the newPostForm template:

<form>
<p>Title: <input type="text" id="title" /></p>
<p>Body: <textarea id="body"></textarea></p>
<p>Author: <select id="author">
<option>David Turnbull</option>
<option>Bob Dole</option>
<option>Mary Lamb</option>
</select></p>
<p><input type="submit" value="Add Post" /></p>
</form>

We haven’t styled our form, so it’s not going to look particularly pretty, but while I believe in the design-first approach, that doesn’t mean we need to get everything pixel perfect. It’s fine to take the “good enough” approach. With this code in place, our form should appear in the browser.

At this point, without filling out any of the form fields, click on the “Add Post” button.

What happens?

You’ll notice two things:

  1. The web application reloads.
  2. Our form’s data is turned into a query string (visible in the location bar).

Here’s a glimpse of this occurring (pulled from another tutorial I’m working on):

preventDefault

To stop this from happening, we need to prevent the default behaviour of the form from occurring. Only then can we make it so data is extracted from the form and inserted into the database. At the moment, the default behaviour will interrupt what we want to do.

Within the isClient conditional of your JavaScript file, create an event:

Template.newPostForm.events({
// events go here
});

The event itself will be of the submit type:

'submit form' : function(){
// code goes here
}

With this code in place, this function will execute whenever we submit our form. To test that it works, use a log statement:

'submit form' : function(){
console.log("Form submitted");
}

If you have multiple forms within a single template, you could specify a form with an ID:

'submit form#YourFormID' : function(){
console.log("Form submitted");
}

But this doesn’t fix the original problem. To achieve that, change the event to this:

'submit form' : function(event){
event.preventDefault();
}

The “event” argument within our function allows us to reference and manipulate the event itself while the function executes. You can see this through the code in our function:

event.preventDefault();

We’re executing the preventDefault() function on our submit event and it’s this code that stops our form from holding onto its default behaviour. Save the file and test your web application in the browser. The submit button should no longer do anything when pressed.

From here, we need to do two things:

  1. Extract the data from our form fields.
  2. Insert that data into our collection.

To achieve that first step, change our event code to this:

'submit form' : function(event, template){
event.preventDefault();
}

The only change we’ve made is adding the “template” argument to our function. This allows us to now reference elements from our template inside of our event. For example, we can write:

template.find("input#title");

And, when placed inside of our event, this line finds the input field with the “title” ID.

We can then extract the value of that input field:

template.find("input#title").value;

Then place this line within a variable so it’s easily accessible:

var titleVar = template.find("input#title").value;

Place this statement and a log statement inside the submit event we created:

var titleVar = template.find("input#title").value;
console.log("The title is " + titleVar);

Whenever we press our submit button now, a log statement will appear in the JavaScript Console that includes the content of whatever’s in our #title text field.

Neat, right?

The exact same process applies to all of our form fields, which means to access the code to access the title, the body, and the author, we can write this:

var titleVar = template.find('input#title').value;
 var bodyVar = template.find('textarea#body').value;
 var authorVar = template.find('select#author').value;

As for inserting this data into the collection, it’s just the code we’ve already come across but, instead of hard-coding the data, we reference our variables:

OurPosts.insert({
 title: titleVar,
 body: bodyVar,
 author: authorVar
 });

After saving the JavaScript file, the form should work as planned. You enter data, press the submit button, and that data is inserted into the collection and appears in the interface.

Removing Data

We’re a few hundred steps from creating a real blogging application but let’s use the remove() function to give us the functionality of deleting our posts.

Now, once upon a time we could write something like this in the JavaScript console to remove all of the data from a collection:

OurPosts.remove();

As noted by Tomas down in the comments though, this produces an error:

Not permitted. Untrusted code may only remove documents by ID. [403]

Basically, we’re unable to use the remove() function on the client unless we can specify the precise ID of what we’re trying to remove. Therefore, we can choose to either:

  1. Manually specify the ID of the document we want to remove.
  2. Become familiar with the remove() function by running it on the server.

The second option is a little more interesting, so let’s do that.

How To Create a Delete Button

To begin, modify our “postsList” template so it looks like this:

<ul>
{{#each items}}
<li>{{title}}, by {{author}} [<a href="#" class="deleteItem">x</a>]</li>
{{/each}}
</ul>

What we’ve done here is created a link so we have something to click. In the coming steps, we’ll make it so, when this link is clicked, the associated blog post will be removed from our interface and from our database. (It’ll also disappear instantly because of Meteor’s magic.)

To continue, switch back to the JavaScript file and setup the code for our events:

Template.postsList.events({
// events go here
});

All of our delete buttons have the “deleteItem” class attached to them, which means we can write an event that looks like this:

'click .deleteItem' : function(){
// code goes here
}

Any code within that block will run when any of those .deleteItem buttons are clicked. To check that this functionality is working, use a log statement:

'click .deleteItem' : function(){
console.log("You clicked a link");
}

If everything’s hooked up correctly, the “You clicked a link” string should appear in the JavaScript Console whenever you click one of the .deleteItem links in our list.

Before we integrate the remove() function, change the log statement to this:

console.log("You clicked this link " + this._id);

If this looks weird, fear not. I’m not expecting you to understand what’s going on. Make the change anyway though, click on the links, and look at the JavaScript Console.

These combinations of numbers and letters are the unique IDs — the primary keys — of the documents within our collection and are automatically created by MongoDB. When we want to reference the ID field, we place an underscore in front of it:

_id

This, for instance, would be a valid use of the find() function:

OurPosts.find({
_id: "6iTGf9FfwFtvk24Nu"
});

Or even the remove() function:

OurPosts.remove({
_id: "6iTGf9FfwFtvk24Nu"
});

It’s just that we wouldn’t ever hard code the ID like this because the IDs are randomly generated and because we’re able to reference _id when need be.

What can be a little trickier to understand though is the use of the “this” keyword:

this._id

If you’re familiar with “this” in JavaScript, you’d know that its value changes depending on the context. In the context of our event, “this” refers to the document being iterated through within our “each” loop from the template.

Confused?

It honestly doesn’t matter if you don’t “get” the conceptual nature of the “this” keyword. You just need to see how we’re using it to retrieve the IDs of our documents. With that knowledge, we can write:

OurPosts.remove({
_id: this._id
});

This code retrieves the document where the special “_id” property is equal to the ID of the current document, and then it removes that document from the collection.

Place this remove() function within our event, save the file, and navigate to the web application in the browser. Click one of the delete links and notice how the blog post disappears.

Exercises

We’ve covered a lot of ground in this tutorial so there’s no reason to feel bad if a lot of it isn’t sinking in. Your best bet at this point is to run through the tutorial once every couple of days for as long as it takes to grasp what’s going on. Repetition is a beginning coder’s best friend.

For those who do generally understand what’s going on, try these extra steps:

  • Change the form so it has two submit buttons. One should save posts as drafts while the other should save them as published posts. Display these posts in different lists.
  • Use everything we’ve learned here to create an application that allows you to insert, display, and remove data. Do this without looking at the tutorial.

What Next?

With this basic understanding of forms and collections, there’s a lot of simple applications we can start attempting to build. You might also have an easier time understanding Meteor’s docs, so consider giving them a read and integrating any functionality that you read about.

Also make sure to follow @MeteorTips on Twitter for more tutorials in the future.

8 comments… add one

  • Aly June 4, 2014, 3:16 pm

    Been using Meteor for about 2 months now. Wish I’d seen this article earlier, but great read nonetheless.

    • David June 4, 2014, 3:31 pm

      Thanks, Aly. Hopefully I can have tutorials more suited for your level in the future. :)

  • Slava June 7, 2014, 2:03 am

    You missed quotes around _id value in the examples of find and remove by _id

    • David June 7, 2014, 7:18 am

      Whoops. My bad. Fixed it up. :)

  • Tomas Wislon June 9, 2014, 5:23 am

    This is an enjoyable little first steps with Meteor but it should be pointed out that as of a few updates ago, the following doesn’t work in the context of “untrusted code”, such as via the console (or even on the client):

    OurPosts.remove({
    isPublished: true
    });

    // produces: Not permitted. Untrusted code may only remove documents by ID. [403]

    And OurPosts.remove() is similarly ineffectual.

    It seems removing categories of data like you are attempting to do ought to be done on the server side through a Meteor method, or otherwise.

    In the meantime, removing them by _id does work:

    OurPosts.remove({
    _id: "6iTGf9FfwFtvk24Nu"
    });

    This slight (hopefully constructive) criticism aside, this was nice little first steps tutorial. Good job.

    • David June 9, 2014, 6:13 am

      Ah, noted. Not sure how that slipped past me. Will fix it up soon. Thanks. :)

  • Andy July 2, 2014, 4:20 am

    Thanks for a great intro to mongoDB with Meteor.JS! The only slightly frustrating parts for a newbie was a couple of typos (although a good exercise in debugging); Outposts and Templates:

    OutPosts.find().fetch();

    Templates.newPostForm.events({
    // events go here
    });

    And it would be nice if you would add “isPublished: true” to this part so that there will be more data to play around with than the one row. It will also let you see stuff being added, which is nice.

    OurPosts.insert({
    title: titleVar,
    body: bodyVar,
    author: authorVar
    });

    My last suggestion would be to add a small section about dropping collections to close the circle. I dug around a bit and it seems the only way to do this is on the server console in the mongoDB command line interface (CLI). Like:


    $ meteor mongo
    > show collections
    > db.posts.drop()

    For those who wants to see even more mongoDB commands I’d recommend trying this on the CLI:

    help
    db.posts.help()

    Peace!

    • David July 2, 2014, 9:56 am

      Thanks for the comment, Andy. I fixed the typos and will probably rewrite this article in the future to include your suggestions. :)

Leave a Comment