Skip to content

Instantly share code, notes, and snippets.

@soheilhy
Created June 27, 2014 21:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save soheilhy/30e503d1c6b3215c034f to your computer and use it in GitHub Desktop.
Save soheilhy/30e503d1c6b3215c034f to your computer and use it in GitHub Desktop.

MongoDB + NodeJS Tutorial

Prerequisites

  • You have node.js and npm installed on your machine.
  • If you use CDF:
    • Use bash or zsh as your shell.
    • Make sure npm installs packages locally as you don't have root permissions. To do so, you can edit ~/.npmrc and add the following line:
      • prefix=$NODE_HOME
    • $NODE_HOME is the directory in which you have installed node.js.

Installation

On Your Personal Machine

To install MongoDB on your machines follow the official installation document.

On CDF

It is similar to installing MongoDB on any 64-bit linux system:

First download the prebuilt binaries:

curl -O http://downloads.mongodb.org/linux/mongodb-linux-x86_64-2.6.3.tgz

Then uncompress the archive and rename the folder to mongodb:

tar -xvzf mongodb-linux-x86_64-2.6.3.tgz && mv mongodb-linux-x86_64-2.6.3 mongodb

Go to the newly created directory and create a folder for your data:

cd mongodb && mkdir data

You are all set.

To run MongoDB, simply run the following command in your MongoDB folder:

bin/mongod --port $PORT --dbpath data/

$PORT is your MongoDB port number. Get one for your instructor.

To connect to MongoDB, you can simply run:

bin/mongo --port $PORT

You can set PATH for convenience:

export PATH=$PATH:$MONGODB_HOME/bin

Access Control in MongoDB

By default, MongoDB does not authenticate users. But, to make it secure, we want to enable authentication on MongoDB. Let's say we want to secure database test. To do so, we need to connect to the database:

mongo --port $PORT test

And then run the following command:

db.createUser(
  {
    user: "testAdmin",
    pwd: "password",
    roles:
    [
      {
        role: "dbOwner",
        db: "test"
      }
    ]
  }
)

This command creates user testAdmin with password password.

Now you need to restart MongoDB (mongod):

bin/mongod --port 12345 --shutdown --dbpath data  # Shutdown
bin/mongod --dbpath=data --port 12345 --auth      # Start

Try to connect to the test db again:

mongo --port $PORT test

And then run show collections in the db console. You'll get an error!

> show collections
2014-06-27T13:37:00.393-0400 error: {
        "$err" : "not authorized for query on test.system.namespaces",
        "code" : 13
} at src/mongo/shell/query.js:131

Let's authenticate when connecting to the db:

mongo --port $PORT -u testAdmin -p password test

Now, you should be able to run show collections.

Accessing MongoDB from Node.JS

There are several options for connecting Node.JS to MongoDB. In this tutorial, we use mongoose.

Installation

To install mongoose simply run:

npm install mongoose

MongoDB Connection

To connect to MongoDB's test database, you need to write the following JavaScript snippet (Note the $PORT in the connection string):

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:$PORT/test', {
  user: 'testAdmin',
  pass: 'password'
});

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
  console.log('Connected to MongoDB');
});

Schemas & Models

mongoose you can define the structure of your data using a schema which is simply a dictionary of fields in your data. Let say we want to model a system to store books. For that we need to define a Book schema:

var BookSchema = mongoose.Schema({
  title: {type: String, trim: true},
  authors: [
    {
      first: String,
      middle: String,
      last: String
    }
  ],
  year: Number
});

In this schema, a Book is defined to have a title, an array of authors and a publication year. title is a string that will be automatically trimmed if needed. authors is a array of first name, middle name, and last name of authors. And year is simply a number.

Using an schema, you can build models. Models are like JavaScript classes that you can instantiate from, save them in the DB, ...:

// Creates the model for Books.
var Books = mongoose.model('Books', bookSchema);

To create an instance from a model, we can simply call new on the model:

// Instantiates a new Book. It's still not in the DB.
var clrs = new Books({
  title: 'Introduction to Algorithms',
  authors: [
    {
      first: 'Thomas',
      middle: 'H.',
      last: 'Cormen'
    },
    {
      first: 'Charles',
      middle: 'E.',
      last: 'Leiserson'
    },
    {
      first: 'Ronald',
      middle: 'L.',
      last: 'Rivest'
    },
    {
      first: 'Clifford',
      last: 'Stein'
    }
  ],
  year: 1990
})

The instnace is not saved in the DB yet. Let's try to save it using save:

// Tries to save the book in the DB.
clrs.save(function (err) {
  if (err) {
    console.log(err);
    return;
  }

  // Now it's saved!
  console.log('Saved : ' + clrs);
});

Now to double-check if it's in the DB, we can find it using its automatically assigned ID, _id:

clrs.save(function (err) {
  if (err) {
    console.log(err);
    return;
  }

  // Now it's saved!
  console.log('Saved : ' + clrs);

  // Let's retrieve it using its ID.
  Books.findById(clrs._id, function(err, book) {
    if (err) {
      console.log(err);
      return;
    }

    console.log('Found CLRS: ' + book);
  });
});

When put together, it'll look like this:

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:12345/test', {
  user: 'testAdmin',
  pass: 'password'
});

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
  console.log('Connected to MongoDB');
});

var bookSchema = mongoose.Schema({
  title: {type: String, trim: true},
  authors: [
    {
      first: String,
      middle: String,
      last: String
    }
  ],
  year: Number
});


var Books = mongoose.model('Books', bookSchema);

var clrs = new Books({
  title: 'Introduction to Algorithms',
  authors: [
    {
      first: 'Thomas',
      middle: 'H.',
      last: 'Cormen'
    },
    {
      first: 'Charles',
      middle: 'E.',
      last: 'Leiserson'
    },
    {
      first: 'Ronald',
      middle: 'L.',
      last: 'Rivest'
    },
    {
      first: 'Clifford',
      last: 'Stein'
    }
  ],
  year: 1990
})

clrs.save(function (err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log('Saved : ' + clrs);

  Books.findById(clrs._id, function(err, book) {
    if (err) {
      console.log(err);
      return;
    }

    console.log('Found CLRS: ' + book);
  });
});

Express + Mongoose

It is assumed that you know how to use express.js.

Let's create a web app that allows us to create, list, update, and delete books. To do so, we'll use express and mongoose.

Router

In your exress application, create a router that handles /books in routes/books.js:

var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:12345/test', {
  user: 'testAdmin',
  pass: 'password'
});

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
  console.log('Connected to MongoDB');
});

var BookSchema = mongoose.Schema({
  title: {type: String, trim: true},
  authors: [
    {
    first: String,
    middle: String,
    last: String
  }
  ],
  year: Number
});

var Books = mongoose.model('Books', BookSchema);

router.get('/', function(req, res) {
  // Lists the books.
});

// This should use POST but we use GET for brevity.
router.get('/new', function(req, res) {
  // Creates a new book.
});

// This should use POST but we use GET for brevity.
router.get('/delete/:id', function(req, res) {
  // Deletes a book using its ID.
});

router.get('/:id', function(req, res) {
  // Returns the information of a particular book.
});

module.exports = router;

In the books router we have:

  • defined the schema.
  • defined the model.
  • defined routes for listing, creating, deleting, and reading a book.

NOTE: The urls we use here are mainly for your convinience. In your projects you must use appropritate verbs (e.g., POST, PATCH, ...).

/books/new

Let's implement new first. To create a book and store in the DB, we simply need to instantiate the model and then save it into our mongodb server:

// This should use POST but we use GET for brevity.
router.get('/new', function(req, res) {
  // Instanitate the model.
  var book = new Books({
    title: req.query.title,
    authors: [
      {
        first: req.query.first,
        middle: req.query.middle,
        last: req.query.last,
      }
    ],
    year: req.query.year
  });

  // Save it to the DB.
  book.save(function(err) {
    if (err) {
      res.status(500).send(err);
      console.log(err);
      return;
    }

    // If everything is OK, then we return the information in the response.
    res.send(book);
  });
});

To test this feature, restart your express app and then browse to http://HOST:PORT/books/new?title=Sometitle&first=Me&last=You&year=2014

Change the request parameters to create other books!

/books/

Now let's implement the feature that allows us to list all the books stored in the DB. To do so, all you need to do is calling Books.find:

router.get('/', function(req, res) {
  Books.find(function(err, books) {
    if (err) {
      res.status(500).send(err);
      console.log(err);
      return;
    }

    // Send the books back to the user.
    res.send(books);
  });
});

To test this feature, restart your express app and then browse to http://HOST:PORT/books/

Do you see the book you just created?

/books/:id

Now let's try to return the information of a single book. To do so, we first get the ID from the user request and then we use Books.findById to retrieve the book:

router.get('/:id', function(req, res) {
  Books.findById(req.params.id, function(err, book) {
    if (err) {
      res.status(500).send(err);
      console.log(err);
      return;
    }

    // If the book is not found, we return 404.
    if (!book) {
      res.status(404).send('Not found.');
      return;
    }

    // If found, we return the info.
    res.send(book);
  });
});

Find a book ID, and try it by using http://HOST:PORT/books/ID in your browser.

/books/delete/:id

Deleting a book is very similar to finding one. You just need to call Books.remove:

router.get('/delete/:id', function(req, res) {
  Books.remove({_id: req.params.id}, function(err) {
    if (err) {
      res.status(500).send(err);
      console.log(err);
      return;
    }

    res.send('Deleted.');
  });
});

Super easy, eh?

Exercise

Modify /books/new to be able to get an array of authors not only one author.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment