MongoDB driver tips & tricks: Node.js Mongoose ODM

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:

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

Update 10/6/14: Depending on your Mongoose version, Mongoose accepts Standard URIs per https://github.com/LearnBoost/mongoose/issues/2246#issuecomment-58039780. It’s unclear what version of the driver first implemented this change.

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

Update (5/21/14 10:34 AM): The MongoDB Node Native driver has pushed a fix for this issue in v1.4.5. We’re unclear when this will be incorporated into the Mongoose driver, but will update accordingly.

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.

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).

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!

Subscribe

Subscribe to our e-mail newsletter to receive updates.