The aim of this tutorial is to take away some of the hassle when using MongoDB with Node.js. Last time out we used the Node.js native MongoDB driver to get data out of a MongoDB database. This worked well, and retrieved our data. But it did seem like an awful lot of code and callbacks for such a simple operation.

Here’s an easier way.

Introducing Mongoose!

In their own words Mongoose provides – “elegant mongodb object modeling for node.js” and “solves common problems for real-world applications”. Essentially, for our purposes here, it exposes only what you need to see and does everything else behind the scenes.

Installing Mongoose

First things first, you’ll need to install Mongoose (assuming you already have Node.js, npm and MongoDB installed).

cd into the root directory of your site – I’ve made a copy of my “testpage-site” folder called “testpage-mongoose” – and enter:

sudo npm install mongoose

Now we’re ready to go!

Taking our code from last time as a start point, we are going to need to look at our controller file, and our model file.

Changing the model file

Most of our work here will be in the ‘model’ file, as we are dealing directly with the database, so let’s start there. For this I have created a new file ‘mongoose-data.js’ using ‘mongo-data.js’ from the previous tutorial as a template.

Connecting to MongoDB using Mongoose

This will be where Mongoose is going to manage the connection to our MongoDB database, so let’s start off with this code snippet:

var mongoose = require('mongoose'),
 db = mongoose.createConnection('localhost', 'euro2012');
db.on('error', console.error.bind(console, 'connection error:'));

Note that we now require ‘mongoose’ instead of ‘mongodb’. We have also very easily created a connection to our database ‘euro2012′ on the server ‘localhost’. Finally, we have created a default error handler to catch any connection errors.

A note on connecting and disconnecting

As Aaron Heckmann has pointed out in the comments – and he knows what he’s talking about – it is “ best to open your db connection once, at application startup, and leave it open until your app shuts down”. Check out my post Mongoose Connection best practice for more details on how to do this.

Using Node.js and Mongoose to query MongoDB

Let’s dive straight into the meat of the code and see what is happening in our ‘export’ function. The export function itself expects two parameters, a variable ‘gname’ that we will search for in the database, and a callback function that we’ll execute when the database query is complete.

exports.teamlist = function(gname,callback){
 db.once('open', function(){
  var teamSchema = new mongoose.Schema({
   country: String,
   GroupName: String
  });
  var Team = db.model('Team', teamSchema);
  Team.find({'GroupName':gname}, function (err, teams) {
   if(err){
    onErr(err,callback);
   }else{
    mongoose.connection.close();
    console.log(teams);
    callback("",teams);
   }
  })// end Team.find
 });// end db.once open
};

First, we open the database in the db.once wrapper, and send the rest of the code through as a callback to be executed once the database connection is open.

Setting a schema and building a model

Mongoose requires a schema to be set for the data. This enables Mongoose to then build a model framework for the information we will be dealing with. Ours is pretty simple here, using just a couple of strings – see the Mongoose docs for more info on schemas.

Here’s our schema:

var teamSchema = new mongoose.Schema({
 country: String,
 GroupName: String
});

And here’s the simple one liner to build a model ‘Team’ from the schema:

var Team = db.model('Team', teamSchema)

Using Mongoose “find” to query MongoDB

The “find” method of Mongoose is very similar to that of the native driver, and is happy to accept the same filter object as an argument. You simply apply the find method to the model you’ve just created, sending the filter object/search query as the first parameter, and a callback function as the second. This callback function has two possible parameters, an error object and the returned data following the query.

Team.find({'GroupName':gname}, function (err, teams) {
 if(err){
  onErr(err,callback);
 }else{
  mongoose.connection.close();
  console.log(teams);
  callback("",teams);
 }
})// end Team.find

The Mongoose ‘find’ method automatically returns an array of JSON objects, reducing the amount of coding effort you have to go to in order to make the data more usable. Our console.log from the above snippet returns something along the lines of:

[ { _id: 50011ff173ca2c992a7bae46,
 country: 'England',
 GroupName: 'D' },
 { _id: 500122cb73ca2c992a7bae48,
 country: 'France',
 GroupName: 'D' },
 { _id: 5001230673ca2c992a7bae49,
 country: 'Sweden',
 GroupName: 'D' },
 { _id: 5001231073ca2c992a7bae4a,
 country: 'Ukraine',
 GroupName: 'D' } ]

Once we have the data we send it through to the callback function provided when the exports function was called.

Catching errors

The last part of our model file deals with catching and returning errors. You can see that if there is an error with the find operation we call this function onErr(err,callback).

var onErr = function(err,callback){
 mongoose.connection.close();
 callback(err);
};

This simply closes the database connection and then sends the error object back to the original callback function.

Using the data in the web page

To get the returned JSON data into our website we need to set up the controller. I have set up a new copy of the controllers/home-mongo.js file and called it home-mongoose.js.

Here is the full source code for that file:


var template = require('../views/template-main');
var mongo_data = require('../model/mongoose-data');

exports.get = function(req, res) {
 var strGroup = "D";
 mongo_data.teamlist(strGroup,function(err,teamlist){
  if(!err){
   var strTeam = "", i = 0, teamCount = teamlist.length;
   for (i = 0; i<teamCount;) {
    strTeam = strTeam + "<li>" + teamlist[i].country + "</li>";
    i = i+1;
   }
   strTeam = "<ul>" + strTeam + "</ul>"
   res.writeHead(200, {'Content-Type': 'text/html'});
   res.write(
    template.build("Test web page on node.js","Hello there","<p>The teams in Group " + strGroup + " for Euro 2012 are:</p>" + strTeam));
   res.end();
  }
  else{
   res.writeHead(200, {'Content-Type': 'text/html'});
   res.write(
    template.build("Oh dear","Database error","<p>Error details: " + err + "</p>"));
   res.end();
  }
 });
};

First of all, we need to require the new model file.

Secondly, we have changed the way the main ‘get’ function works very slightly by providing the name of the group of teams we want. In this case “D”. This means that we will search the database for this and return the relevant teams (this is what we did in the model at the start of this post).

After that, looping through the list and sending the output to the template is pretty much the same as before.

That’s it!

Well, that’s nearly it. Don’t forget to change the router to require the ‘home-mongoose’ controller instead of ‘home-mongo’.

Now we have Mongoose acting as an interface to our MongoDB database, taking much of the coding pain away and simplifying the process. Coming up next we’ll make life even easier for ourselves by using the Express framework.

Get the source code

The source code for this and my other tutorials on Node.js is available on GitHub here: https://github.com/simonholmes/knowing-node/

  • Find this useful? Check out my book, Knowing Node