Data driven NodeJS tutorials Part 3 - Integrating models, Mongoose and MongoDB

Hi all,

This is the third part of my tutorial series which focuses on NodeJS in a purely data-driven fashion - for those who like to keep their backends and front-ends distinct, this series should provide a quick kickstart.

I'm pushing the code from each tutorial into this public git repo:

https://github.com/philhudson91/data-driven-nodejs-tutorials

This part of the tutorial will provide the basics for integrating a database (MongoDB) and provide guidance toward integrating models and Mongoose.

Let's get started then!

Step 1 - Find your code from last time

For the third part, we will be re-using and re-working the code from the last part of the tutorial series.

You can find the previous part here, and all of the code on Github here.

Once you've got the code in front of you, make sure it's all okay by running:

mocha - to re-run the test.

node app.js - to check the app runs.

If all is well, let's move onto the next step.

Step 2 - Add Mongoose as a dependency and install

To integrate MongoDB a package that really helps is Mongoose.

Mongoose provides elegant MongoDB object modelling for NodeJS.

I won't go into detail about Mongoose now, let's just get it installed.

Open up your package.json file and add the following line to the dependencies object:

"mongoose": "^4.0.0"

If you inserted it correctly, the package.json should look like the following:

package.json:

{
  "name": "application-name",
  "version": "0.0.1",
  "dependencies": {
    "body-parser": "^1.0.2",
    "express": "^4.0.0",
    "request": "^2.67.0",
    "async": "^0.8.0",
    "mongoose": "^4.0.0"
  },
  "devDependencies": {
    "chai": "^1.9.1",
    "chance": "^0.5.9",
    "mocha": "^1.18.2",
    "node-inspector": "^0.12.3",
    "supertest": "^0.13.0"
  }
}

Cool!

Now let's get it installed, run npm install and the new dependency will be installed.

Step 3 - Get a local MongoDB running

Before we go any further, we need a database running locally.

I won't go into detail about installing MongoDB here as there are other articles that detail it better, but for this step ensure you have installed Mongo in your local environment.

Installation guides can be found for each platform here:

Install MongoDB on OSX

Install MongoDB on Ubunutu

Install MongoDB on Windows

Once installed, make sure Mongo is running, on OSX run mongod.

Also note the path to MongoDB - usually it is mongodb://localhost:27017.

Awesome so Mongo should be running nicely. Let's get writing some code to insert some basic stuff into the database.

Step 4 - Getting our app ready for Mongo, getting connected

Head to the app.js file and add mongoose to the imports:

var mongoose = require('mongoose');

Nice and simple! Now, let's create a connection to our local MongoDB.

Head down the app.js file and include the connection above where we set up the server. (I'll show a full app.js file in a minute so don't worry if you're lost!).

We need to set up Mongo before we start the server, otherwise the server will not run.

Let's add the code in!

var mongoDB = 'mongodb://localhost:27017/test';
mongoose.connect(mongoDB);
var db = mongoose.connection;
mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:'));

So in these four lines of code we've done quite a lot. We've created a variable for the path to our local MongoDB.

We've then used mongoose.connect(mongoDB) to connect to the database.

Then we assign the mongoose.connection to a db variable.

And finally we've set up a handler to catch errors from MongoDB. This last part is very important as a mishandled or unhandled error can take your NodeJS app down!

Cool. Let's check if our app still works okay.

mocha - to re-run the test.

node app.js - to check the app runs.

If all is well you should see:

Express server listening on port 3000

Step 5 - Creating the cat model

Now we're pretty much ready to start doing some cool stuff! :D

Let's start by creating our first model. Create the file models/CatModel.js.

In this file we'll start be importing the mongoose module, as such:

var mongoose = require('mongoose');

Then instantiate the mongoose.schema object and objectId for use within our code:

var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

And now let's create a basic schema, using the schema object we just instantiated:

var CatSchema = Schema({
    id: ObjectId,
    fluffiness: {
        type: Number,
        required: true
    }
});

The schema is super simple! It only contains an ObjectId and a property 'fluffiness', which is a Number and is required.

Then let's export our Cat model so it is publicly accessible:

exports.Cat = mongoose.model('Cat', CatSchema);

Leaving the final models/CatModel.js looking like:

var mongoose = require('mongoose');

var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

var CatSchema = Schema({
    id: ObjectId,
    fluffiness: {
        type: Number,
        required: true
    }
});

exports.Cat = mongoose.model('Cat', CatSchema);

Step 6 - The cat 'create' route and the relationship between the model and the controller

Nice, this is coming on nicely.

Now let's create the route in the routes.js file:

router.get('/add', mainController.catCreate);

Like before, this makes the routers listen for get requests at the path 'add' and then passes the request to be handled by mainController.catCreate.

So that the entire file looks like:

var express = require('express');
var router = express.Router();
var mainController = require('./controllers/mainController');

/* Map URLs to handlers in this file */

router.get('/', mainController.defaultEndpoint);

router.get('/add', mainController.catCreate);

module.exports = router;

Finally, we need to make the modifications to our controller. As with the previous tutorial we will again be using async.waterfall.

As we are creating a method that utilises our CatModel.js file, we'll need to import it like so:

var Cat = require('../models/CatModel').Cat;

And then we will create the following method. I'll paste the whole code here and explain it below:

exports.catCreate = function(req, res) {

    var fluffiness = function (callback) {
        var amount = Math.floor((Math.random() * 10) + 1);

        if (amount) {
            return callback(null, amount);
        } else {
            return callback('Error fluffiness amount failed', null);
        }
    };

    var create = function (amount, callback) {

        var data = {
            fluffiness: amount
        };

        Cat.create(data, function(error, success){
            if (error) {
                return callback(error);
            }

            return callback(null, success);
        })
    };

    async.waterfall([fluffiness, create], function (err, result) {

        if (err) {
            return res.status(500).json({message: err});
        }
        return res.status(200).json(result);
    });
};

It looks to be quite a long method, but actually it's quite simple.

I've created the method to be exported as catCreate. It then breaks into separate functions for the async.waterfall process. The first function is fluffiness which simply returns a random number between one and ten for the cat's fluffiness - the same as the function in the previous tutorial for returning the amount of cats.

The second function is where the database action happens. We use Mongoose's native method create which models inherit by default. I create the data object and use fluffiness as the property and the amount variable as the value. This has to match your schema where the model was declared.

And why didn't we add in the ObjectId? Because it is automatically generated by Mongo for us. :)

Then after that function runs, we handle it's callback and return the new object created in the database as JSON to the end user. Note, we've modified the return statement at the bottom of the async waterfall to directly return the result object.

We can test to see if it works by running the app with node app.js and then navigating to http://localhost:3000/add in the browser. If all is well, you should see a new cat on every refresh! :D

Step 7 - Write a test

Yep it's test time again.

For a very basic test, let's check that the endpoint returns data when requested and that it contains the properties _id and fluffiness which we expect to be returned when a new object is created.

Head into the test.js file and we'll add the following code:

describe('get cat add endpoint', function () {

    it('responds with 200', function (done) {

        api.get('/add')
            .expect(200)
            .end(function (err, res) {

                if (err) {
                    return done(err);
                }

                expect(res.body).to.have.a.property('_id');
                expect(res.body).to.have.a.property('fluffiness');

                done();

            });
    });

});

As you can see, we've used a similar pattern to the previous test and only really added in the two expect() lines. This are basic assertions that check that the response we receive is essentially what we expect. We expect there to be properties _id and fluffiness but we do not know what value they will contain (as of course they are randomly generated).

Let's run it!

Hit mocha in the terminal - the output should be:

  get main endpoint
    ✓ responds with 200

  get cat add endpoint
    ✓ responds with 200


  2 passing (42ms)

Awesome, they pass! :D

Summary

In this tutorial we covered a lot of ground - connecting our example app to MongoDB using Mongoose, creating an endpoint that creates a new document in Mongo and writing a test to test the endpoint.

As usual, please comment or tweet me if you have any questions.

See all the parts below:

Part 1 - Basic server with test

Part 2 - Using controllers and basic async operations

And the code on Github here.

Phil Hudson

Read more posts by this author.