By Mike Pateras


2012-08-19 21:43:07 8 Comments

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.

3 comments

@Amol M Kulkarni 2013-06-07 10:51:53

ES 6 (Node 8+)

You can utilize async/await

await operator pauses the execution of asynchronous function until the Promise is resolved and returns the value.

This way your code will work in synchronous way:

const query = MySchema.findOne({ name: /tester/gi });
const userData = await query.exec();
console.log(userData)



Older Solution - June 2013 ;)

Now the Mongo Sync is available, this is the right way to make a synchronous MongoDB query in Node.js.

I am using this for the same. You can just write sync method like below:

var Server = require("mongo-sync").Server;
var server = new Server('127.0.0.1');
var result = server.db("testdb").getCollection("testCollection").find().toArray();
console.log(result);

Note: Its dependent on the node-fiber and some issues are there with it on windows 8.

Happy coding :)

@jcollum 2013-07-23 17:36:37

I coded a 5 line script with mongo-sync and it failed, even though it matched their test code nearly perfectly. It seems to have bugs.

@Amol M Kulkarni 2013-07-24 05:46:45

@jcollum : Can you please describe the exact issue you had? because its working for me with no major issues.. If you are sure its a bug in module you can raise a new issue on Repo

@jcollum 2013-07-24 17:17:48

I submitted a bug. Apparently you have to delete the fibers modules from node_modules in the mongo-sync lib. Looks like a packaging problem.

@Esailija 2014-04-18 09:45:24

If it's dependent on node-fiber then it's not synchronous

@JohnnyHK 2012-08-19 22:03:08

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.

@Logan 2012-12-14 12:38:09

Thanks Johnny for this workaround! I wish there was a simple way out of the box... it is frustrating even to write a simple if_exists() function... Btw, if anybody knows an easier way, or an update from the driver, please post it here.

@ElHacker 2012-12-15 03:25:42

You can always use an async library, to avoid the identation of "doom" github.com/caolan/async That will make the code more readable and nice.

@PeterT 2015-03-08 02:44:08

@Logan I wouldn't call it a "workaround", that is how node is designed to work.

@Rishitha Minol 2018-05-29 10:36:15

returned thing is a '[object Promise]'. How can we read data inside promise object? (without .then callback)

@JohnnyHK 2018-05-29 13:53:48

@RishithaMinol You can await the promise from within another async function: let thing = await getAThing();

@user1063287 2018-11-13 14:33:59

I am trying to move away from the callback function model where it is easy to handle an error or success result, ie function(err, result) { if (err) { res.send(err) } else { // do things } - how do you handle/view an error that comes back from let thing = await db.collection.findOne(query)?

@JohnnyHK 2018-11-13 14:46:58

@user1063287 With await you have to use try/catch blocks for error handling.

@user1063287 2018-11-13 14:52:34

@JohnnyHK - like this? try { var bob = await getAThing() } catch(err) { console.log("mongodb query error is here: " + err ) }

@JohnnyHK 2018-11-13 15:09:09

@user1063287 Yes. See here for more examples.

@Hugheth 2016-05-20 13:56:06

While it's not strictly synchronous, a pattern I've repeatedly adopted and found very useful is to use co and promisify yield on asynchronous functions. For mongo, you could rewrite the above:

var query = co( function* () {

    var db = new mongo.Db("mydatabase", server, {});
    db = promisify.object( db );
    db = yield db.open();

    yield db.authenticate("myuser", "mypassword");

    var collection = yield db.collection("Things");
    return yield collection.findOne( { name : "bob"} );

});

query.then( result => {

} ).catch( err => {

} );

This means:

  1. You can write "synchronous"-like code with any asynchronous library
  2. Errors are thrown from the callbacks, meaning you don't need the success check
  3. You can pass the result as a promise to any other piece of code

Related Questions

Sponsored Content

34 Answered Questions

68 Answered Questions

[SOLVED] What is the most efficient way to deep clone an object in JavaScript?

12 Answered Questions

[SOLVED] Node.js + Nginx - What now?

  • 2011-02-15 20:49:02
  • Van Coding
  • 340986 View
  • 976 Score
  • 12 Answer
  • Tags:   node.js nginx concept

52 Answered Questions

[SOLVED] What is the best way to detect a mobile device?

26 Answered Questions

[SOLVED] How to get GET (query string) variables in Express.js on Node.js?

14 Answered Questions

[SOLVED] Check synchronously if file/directory exists in Node.js

  • 2010-12-19 11:19:43
  • Ragnis
  • 721299 View
  • 1134 Score
  • 14 Answer
  • Tags:   node.js

34 Answered Questions

[SOLVED] How to query MongoDB with "like"?

12 Answered Questions

[SOLVED] What is the purpose of Node.js module.exports and how do you use it?

  • 2011-03-15 11:56:10
  • mrwooster
  • 472006 View
  • 1407 Score
  • 12 Answer
  • Tags:   javascript node.js

Sponsored Content