What's the best practice for MongoDB connections on Node.js?
Asked Answered
N

2

39

This is something that is a bit unclear to me (I'm just getting started with Node and Mongo), and it really concerns me because of server performance and strain (which I guess is another question, but I'll get to that at the end of the post).

So, assuming I'm writing an API with Node.js and Restify, where each API endpoint corresponds to a function, should I:

a) open the db connection and store it in a global var, and then just use that in every function?
Example:

// requires and so on leave me with a db var, assume {auto_reconnect: true}
function openDB() {
    db.open(function(err, db) {
        // skip err handling and so on
        return db;
    }
}

var myOpenDB = openDB(); // use myOpenDB in every other function I have

b) open the db connection and then just put everything in one giant closure?
Example:

// same as above
db.open(function(err, db) {
    // do everything else here, for example:
    server.get('/api/dosomething', function doSomething(req, res, next) { // (server is an instance of a Restify server)
        // use the db object here and so on
    });
}

c) open and close the db each time it is needed?
Example:

// again, same as above
server.get('/api/something', function doSomething(req, res, next) {
    db.open(function(err, db) {
        // do something
        db.close();
    });
});

server.post('/api/somethingelse', function doSomethingElse(req, res, next) {
    db.open(function(err, db) {
        // do something else
        db.close();
    });
});

This last one is what I would do out of intuition, but at the same time I don't feel entirely comfortable doing this. Doesn't it put too much strain on the Mongo server? Especially when (and I hope I do get to that) it gets hundreds — if not thousands — of calls like this?

Thank you in advance.

Nurmi answered 3/8, 2012 at 16:47 Comment(1)
J
14

I like MongoJS a lot. It lets you use Mongo in a very similar way to the default command line and it's just a wrapper over the official Mongo driver. You only open the DB once and specify which collections you'll be using. You can even omit the collections if you run Node with --harmony-proxies.

var db = require('mongojs').connect('mydb', ['posts']);

server.get('/posts', function (req, res) {
  db.posts.find(function (err, posts) {
    res.send(JSON.stringify(posts));
  });
});
Jehol answered 3/8, 2012 at 18:40 Comment(4)
I actually love it! :D Thank you so much. But, like I said to Gijs in my answer's comments, I read somewhere that doing everything under the same connection results in blocking code. Do you know if this is true?Nurmi
I don't think so. It'd defeat the purpose. I believe connections to the database are done through sockets. Sockets do over networks something very similar to what Node does when reading files with Streams. A ReadStream reads the file in little chunks and fires events when it receives them. I think sockets do the same with network chunks. See nodejs.org/api/net.html#net_class_net_socketJehol
Blocking or non-blocking depends on the API. So the above example looks like it would be blocking in the sense that the server.get(...) doesn't get executed until you successfully connect to the DB (that's purely what it looks like; I'm not familiar with MongoJS). However, given that you probably need the DB to do anything, that's not necessarily all that bad, although if you have many of these dependencies that take a long time to execute, it'd be better to use a solution with futures/promises/callbacks like in @scttnlsn 's solution.Radius
Note that MongoJS's actual query implementations seem to be async, from what I can tell from the github page, as they all take callback arguments, so there shouldn't be any issues there.Radius
C
6
  • Option A is not a great idea since there is no guarantee that the DB will be finished opening before an HTTP request is handled (granted this is very unlikely)
  • Option C is also not ideal since it needlessly opens and closes the DB connection

The way that I like to handle this is using deferreds/promises. There are a bunch of different promise libraries available for Node but the basic idea is to do something like this:

var promise = new Promise();

db.open(function(err, db) {
    // handle err
    promise.resolve(db);
});

server.get('/api/something', function doSomething(req, res, next) {
    promise.then(function(db)
        // do something
    });
});

I believe Mongoose handles connections in a way that vaguely resembles this.

Chen answered 3/8, 2012 at 17:54 Comment(2)
Thank you. I looked into Mongoose and apparently it doesn't do things very differently from the official driver. It just fires an event when it finishes opening the connection, and while it's not connected it buffers operations so you don't have to worry about it. I'll look into the promise thing, though :)Nurmi
The library presented below, MongoJS, is actually a much better example of using promises (they're called futures though).Chen

© 2022 - 2024 — McMap. All rights reserved.