What is the right way to make a synchronous MongoDB query in Node.js?
Solution 1
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
.
Solution 2
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 :)
Solution 3
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:
- You can write "synchronous"-like code with any asynchronous library
- Errors are thrown from the callbacks, meaning you don't need the success check
- You can pass the result as a promise to any other piece of code
Mike Pateras
Young developer, interested in gaming and game development, along with many other things.
Updated on November 27, 2020Comments
-
Mike Pateras over 3 years
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.
-
Logan over 11 yearsThanks 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 over 11 yearsYou 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.
-
jcollum almost 11 yearsI 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 almost 11 years@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 almost 11 yearsI 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 about 10 yearsIf it's dependent on node-fiber then it's not synchronous
-
PeterT about 9 years@Logan I wouldn't call it a "workaround", that is how node is designed to work.
-
Rishitha Minol almost 6 yearsreturned
thing
is a '[object Promise]'. How can we read data inside promise object? (without.then
callback) -
JohnnyHK almost 6 years@RishithaMinol You can
await
the promise from within anotherasync
function:let thing = await getAThing();
-
user1063287 over 5 yearsI 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 fromlet thing = await db.collection.findOne(query)
? -
JohnnyHK over 5 years@user1063287 With
await
you have to use try/catch blocks for error handling. -
user1063287 over 5 years@JohnnyHK - like this?
try { var bob = await getAThing() } catch(err) { console.log("mongodb query error is here: " + err ) }
-
JohnnyHK over 5 years@user1063287 Yes. See here for more examples.
-
Atul about 2 yearsUnstable package. mongo-sync gives assertion failure at the time of initialization only.