Search code examples
javascriptnode.jsmongodbsynchronous

What is the right way to make a synchronous MongoDB query in Node.js?


I'm using the Node.JS driver for MongoDB, and I'd like to perform a synchronous query, like such:

function getAThing()
{
    var db = new mongo.Db("mydatabase", server, {});

    db.open(function(err, db)
    {
        db.authenticate("myuser", "mypassword", function(err, success)
        {
            if (success)
            {
                db.collection("Things", function(err, collection)
                {
                    collection.findOne({ name : "bob"}, function(err, thing)
                    {                           
                        return thing;
                    });
                });
            }
        });
    });
}

The problem is, db.open is an asychronous call (it doesn't block), so the getAThing returns "undefined" and I want it to return the results of the query. I'm sure I could some sort of blocking mechanism, but I'd like to know the right way to do something like this.


Solution

  • There's no way to make this synchronous w/o some sort of terrible hack. The right way is to have getAThing accept a callback function as a parameter and then call that function once thing is available.

    function getAThing(callback)
    {
        var db = new mongo.Db("mydatabase", server, {});
    
        db.open(function(err, db)
        {
            db.authenticate("myuser", "mypassword", function(err, success)
            {
                if (success)
                {
                    db.collection("Things", function(err, collection)
                    {
                        collection.findOne({ name : "bob"}, function(err, thing)
                        {       
                            db.close();                    
                            callback(err, thing);
                        });
                    });
                }
            });
        });
    }
    

    Node 7.6+ Update

    async/await now provides a way of coding in a synchronous style when using asynchronous APIs that return promises (like the native MongoDB driver does).

    Using this approach, the above method can be written as:

    async function getAThing() {
        let db = await mongodb.MongoClient.connect('mongodb://server/mydatabase');
        if (await db.authenticate("myuser", "mypassword")) {
            let thing = await db.collection("Things").findOne({ name: "bob" });
            await db.close();
            return thing;
        }
    }
    

    Which you can then call from another async function as let thing = await getAThing();.

    However, it's worth noting that MongoClient provides a connection pool, so you shouldn't be opening and closing it within this method. Instead, call MongoClient.connect during your app startup and then simplify your method to:

    async function getAThing() {
        return db.collection("Things").findOne({ name: "bob" });
    }
    

    Note that we don't call await within the method, instead directly returning the promise that's returned by findOne.