NoSQL Zone is brought to you in partnership with:

Developer Advocate at MongoLab. Previously interned at VMware and NetApp. UCSD CSE 2013 Alum. Chris is a DZone MVB and is not an employee of DZone and has posted 21 posts at DZone. You can read more from them at their website. View Full User Profile

MongoDB Driver Tips & Tricks: Mongoose

04.18.2014
| 8059 views |
  • submit to reddit

Many of the support requests we get at MongoLab are questions about how to properly configure and use particular MongoDB drivers and client libraries.

This blog post is the 2nd of a series where we are covering the popular MongoDB drivers in depth (we covered Mongoid last time). The driver we’re covering today is Mongoose, which is maintained by Aaron Heckmann (@aaronheckmann) and officially supported by MongoDB, Inc.

In this post:

Mongoose

Mongoose is a Node.js Object Document Mapper (ODM). For relational folks, ODMs are the MongoDB equivalent of Object Relational Mappers (ORMs). One major reason developers use ODMs like Mongoose is that it gives them the ability to define a schema for their documents which can then be used to map documents to objects in their programming language. With Mongoose, this feature serves as an easy transition for former Ruby on Rails developers used to working with ActiveRecord.

Mongoose is written on top of the native Node.js MongoDB driver which is also officially supported by MongoDB, Inc. While Mongoose defines models and uses them to query, the native Node.js MongoDB  provides a more traditional querying interface. We will cover this native driver in a future installment of this blog series.

This post aims to help you understand how to configure and use Mongoose effectively in your MongoDB application.

A simple Mongoose example

You can find a straightforward example on connecting, inserting, updating and querying using Mongoose in MongoLab’s Language Center.

Production-ready connection settings

We often see that users have problems connecting to MongoLab using the Mongoose driver. The root cause is almost always incorrect configuration of the driver, particularly around timeouts. The following is a connection example using the MongoLab-recommended driver options:

// mongoose 3.8.x
var mongoose = require('mongoose');
// mongodb-uri 0.9.x
var uriUtil = require('mongodb-uri');
 
/* 
 * Mongoose by default sets the auto_reconnect option to true.
 * We recommend setting socket options at both the server and replica set level.
 * We recommend a 30 second connection timeout because it allows for 
 * plenty of time in most operating environments.
 */
var options = { server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } }, 
                replset: { socketOptions: { keepAlive: 1, connectTimeoutMS : 30000 } } };       
 
/*
 * Mongoose uses a different connection string format than MongoDB's standard.
 * Use the mongodb-uri library to help you convert from the standard format to
 * Mongoose's format.
 */
var mongodbUri = 'mongodb://user:pass@host:port/db';
var mongooseUri = uriUtil.formatMongoose(mongodbUri);
 
mongoose.connect(mongooseUri, options);
var conn = mongoose.connection;             
 
conn.on('error', console.error.bind(console, 'connection error:'));  
 
conn.once('open', function() {
  // Wait for the database connection to establish, then start the app.                         
});

...

Additional connection options that are supported by the underlying native Node.js driver can be found here.

Mongoose tips & tricks

Mongoose requires a different connection string format

The Mongoose URI format follows the convention:

mongodb://user:pass@host1:port1/database, mongodb://user:pass@host2:port2,mongodb://user:pass@host3:port3

Notice that the database name is included with the first node.  Also notice that every node requires the “mongodb://” prefix.

This format differs from MongoDB’s standard connection string format which looks like:

mongodb://user:pass@host1:port1,host2:port2,host3:port3/database

In the simple Mongoose example that we referenced above, we use a function in the mongodb-uri package to convert from the standard MongoDB connection string format to the one that Mongoose expects.

Increase connection timeouts to mitigate errors

A common error that we see with the Mongoose driver is Mongo Connection error. For example:

Mongo Connection error: Error: No primary found in set

This error is thrown whenever Mongoose has difficulty making a connection. Depending on the type of connection being created by the driver (server or replica set), the default timeout value varies from 1 to 5 seconds. We often see that users who are running their app with a Platform-as-a-Service (PaaS), such as Heroku, will find that their driver needs longer to establish a connection to the database, particularly when dynos are just starting up. For this reason we recommend allowing up to 30 seconds for connections to become established.

New connections require the write lock; be careful with index builds

In order to establish a connection to your MongoDB, the driver must first connect to and authenticate on all the replica set nodes. The authentication step requires the write lock to complete.

As of MongoDB 2.4.x, index builds on secondaries run in the foreground and hold the write lock. As a result, any connection attempting to authenticate (which requires the write lock) must wait until the index build is complete. To avoid unexpected errors, we recommend building indexes during off-peak times when the application can reuse existing connections that have already authenticated.

This behavior is slightly different from other drivers which can usually handle not being able to authenticate to the secondaries in the replica set cluster.

Properly configure application error handling

Node.js error handling can often be tricky. For newer developers, it’s important to remember that most errors are returned through callbacks. You can handle errors in the callback, or you can consider passing them to a centralized framework by using the Node.js “next” callback convention offered to every Express route.

app.get('/docs', function (req, res, next) {
  Model.find({}, function (err, docs) {
    if (err) return next(err);
  })
})

However, some errors happen outside of the context of an individual database operation. These errors e.g. connection errors, require handlers registered at a broader scope (the connection itself).

var conn = mongoose.createConnection(..); 
conn.on('error', handler);

Aaron Heckmann, the core maintainer of the Mongoose project, provides some good advice on Mongoose and Express error handling in this StackOverflow thread.

You’re all set!

We hope this post helps shed light on some things that are unique to Mongoose. If you have any tips and tricks that you’d like to share please post in comments or write to us at support@mongolab.com so we can pass on the knowledge. Happy hacking!


Published at DZone with permission of Chris Chang, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)