87

I've been trying to discover how to use MongoDB with Node.js and in the docs it seems the suggested way is to use callbacks. Now, I know that it is just a matter of preference, but I really prefer using promises.

The problem is that I didn't find how to use them with MongoDB. Indeed, I've tried the following:

var MongoClient = require('mongodb').MongoClient;

var url = 'mongodb://localhost:27017/example';

MongoClient.connect(url).then(function (err, db) {
    console.log(db);
});

And the result is undefined. In that case it seems this is not the way to do so.

Is there any way to use mongo db inside Node with promises instead of callbacks?

user1620696
  • 10,825
  • 13
  • 60
  • 81

13 Answers13

124

Your approach is almost correct, just a tiny mistake in your argument

var MongoClient = require('mongodb').MongoClient
var url = 'mongodb://localhost:27017/example'
MongoClient.connect(url)
  .then(function (db) { // <- db as first argument
    console.log(db)
  })
  .catch(function (err) {})
Green
  • 4,950
  • 3
  • 27
  • 34
  • 12
    Exactly! MongoDB Node.js driver 2.x _"Returns: Promise if no callback passed"_ i.a. by `MongoClient.connect`. If you have node.js < 4 without ES6 promises built in then you can also use an ES6 compatible promise shim or provide an ES6 compatible promise implementation by the `promiseLibrary` option of `MongoClient.connect`. – VolkerM Sep 30 '16 at 09:30
  • 5
    Based on some testing, if you connect to the URL ```mongodb//localhost:27017``` (without specifying a database) you get back a mongoclient, so you need to call ```mongoclient.db('example')```. See https://mongodb.github.io/node-mongodb-native/api-generated/mongoclient.html#db – PatS Jun 13 '18 at 14:50
32

You can also do async/await

async function main(){
 let client, db;
 try{
    client = await MongoClient.connect(mongoUrl, {useNewUrlParser: true});
    db = client.db(dbName);
    let dCollection = db.collection('collectionName');
    let result = await dCollection.find();   
    // let result = await dCollection.countDocuments();
    // your other codes ....
    return result.toArray();
 }
 catch(err){ console.error(err); } // catch any mongo error here
 finally{ client.close(); } // make sure to close your connection after
}
ginad
  • 1,863
  • 2
  • 27
  • 42
  • 1
    I could kiss your face right now. This was the simplest, best answer I have found in hours. – Rob E. Feb 21 '20 at 00:42
  • This is just the simplest, freshest, and most complete and up-to-date possible answer. Thank you so much. – keuluu May 26 '20 at 22:06
  • For me this threw an error "Cannot use a session that has ended". This was because result.toArray() also returns a Promise. So you would have to use "await result.toArray()" – Passiv Programmer Oct 09 '21 at 13:19
18

Since none of the answers above mention how to do this without bluebird or q or any other fancy library, let me add my 2 cents on this.

Here's how you do an insert with native ES6 promises

    'use strict';

const
    constants = require('../core/constants'),
    mongoClient = require('mongodb').MongoClient;



function open(){

    // Connection URL. This is where your mongodb server is running.
    let url = constants.MONGODB_URI;
    return new Promise((resolve, reject)=>{
        // Use connect method to connect to the Server
        mongoClient.connect(url, (err, db) => {
            if (err) {
                reject(err);
            } else {
                resolve(db);
            }
        });
    });
}

function close(db){
    //Close connection
    if(db){
        db.close();
    }
}

let db = {
    open : open,
    close: close
}

module.exports = db;

I defined my open() method as the one returning a promise. To perform an insert, here is my code snippet below

function insert(object){
    let database = null;
    zenodb.open()
    .then((db)=>{
        database = db;
        return db.collection('users')    
    })
    .then((users)=>{
        return users.insert(object)
    })
    .then((result)=>{
        console.log(result);
        database.close();
    })
    .catch((err)=>{
        console.error(err)
    })
}



insert({name: 'Gary Oblanka', age: 22});

Hope that helps. If you have any suggestions to make this better, do let me know as I am willing to improve myself :)

PirateApp
  • 5,433
  • 4
  • 57
  • 90
  • 15
    You wrap a promise in another promise. MongoClient methods are returning a promise already, and there is no need to wrap this again. This is a typical promise anti pattern. – westor Nov 14 '16 at 21:24
  • 4
    Coming along months later, but @Green's answer 20 minutes after the original post uses mongodb.MongoClient's native promise support and no extraneous promise libraries. – Owen Dec 08 '16 at 18:34
  • 2
    This should be the correct answer, since it doesn't rely on any third party promise libraries. – GlGuru Apr 02 '18 at 17:06
  • @westor how will you return a promise from open() method without wrapping it up in new Promise? I think this is the only way. – Abhishek Nalin Jun 20 '18 at 02:06
  • 1
    @AbhishekNalin MongoDB connect method (at least in newer versions) returns a promise. Therefore you simple could write 'mongoClient.connect(url).then(...)' or in this open method you would return mongoClient.connect(url). You can get rid of the callback. The error case is catched by the the last catch here. – westor Jun 20 '18 at 11:00
  • You are correct. But I think wrapping up in new Promise is required when we have two separate modules to do the task. Like we have a 'routes' module which gets called first on user request and a 'db_schema' module which handles the actual db interaction. So I need to call a function in db_schema from routes to process the request. – Abhishek Nalin Jun 21 '18 at 14:56
11

This is a General answer for How to use MongoDB with promises in Node.js?

mongodb will return a promise if the callback parameter is omitted

Before converting to Promise

var MongoClient = require('mongodb').MongoClient,
dbUrl = 'mongodb://db1.example.net:27017';

MongoClient.connect(dbUrl,function (err, db) {
    if (err) throw err
    else{
        db.collection("users").findOne({},function(err, data) {
            console.log(data)
        });
    }
})

After converting to Promise

//converted
MongoClient.connect(dbUrl).then(function (db) {
    //converted
    db.collection("users").findOne({}).then(function(data) {
         console.log(data)
    }).catch(function (err) {//failure callback
         console.log(err)
    });
}).catch(function (err) {})

Incase you need to handle multiple request

MongoClient.connect(dbUrl).then(function (db) {

   /*---------------------------------------------------------------*/

    var allDbRequest = [];
    allDbRequest.push(db.collection("users").findOne({}));
    allDbRequest.push(db.collection("location").findOne({}));
    Promise.all(allDbRequest).then(function (results) {
        console.log(results);//result will be array which contains each promise response
    }).catch(function (err) {
         console.log(err)//failure callback(if any one request got rejected)
    });

   /*---------------------------------------------------------------*/

}).catch(function (err) {})
Siva Kannan
  • 2,237
  • 4
  • 27
  • 39
  • 1
    Why do you create nested promises chain for operation after connection? Why not: `MongoClient.connect(uri).then(client => client.db("db").collection("users").find()).then(data => console.log(data)).catch(err => console.log(err));` – SerG Jun 23 '18 at 12:07
  • This would be better with links to the documentation – mikemaccana Apr 09 '19 at 12:35
  • Shorter notation for catch: .catch(console.log) – Benjam May 14 '20 at 17:12
3

I know I am a bit late to the party but I'd like to share an example using ES6

const config = require('config');
const MongoClient = require('mongodb').MongoClient;

var _connection;
var _db;

const closeConnection = () => {
  _connection.close();
}

/**
 * Connects to mongodb using config/config.js
 * @returns Promise<Db> mongo Db instance
 */
const getDbConnection = async () => {
  if (_db) {
    return _db;
  }
  console.log('trying to connect');
  const mongoClient = new MongoClient(config.mongodb.url, { useNewUrlParser: true });
  _connection = await mongoClient.connect();
  _db = _connection.db(config.mongodb.databaseName);
  return _db;
}

module.exports = { getDbConnection, closeConnection };

I go a bit into more detail here if you want to take a look:

https://medium.com/swlh/how-to-connect-to-mongodb-using-a-promise-on-node-js-59dd6c4d44a7

  • very nice. I would just rename the function getDbConnection because it does not return the connection. It returns the _db. :) – kroiz Sep 08 '20 at 19:32
2

WARNING Edit:

As John Culviner noted, this answer is deprecated. Use the driver, it comes with promises OOTB.


If you choose to use bluebird as a promise library, you can use bluebirds promisifyAll() function on MongoClient:

var Promise = require('bluebird');
var MongoClient = Promise.promisifyAll(require('mongodb').MongoClient);

var url = 'mongodb://localhost:27017/example';

MongoClient.connectAsync(url).then(function (db) {
    console.log(db);
}).catch(function(err){
    //handle error
    console.log(err);
});
mateos
  • 1,405
  • 1
  • 17
  • 26
Simon Z.
  • 497
  • 4
  • 11
  • 6
    MongoDB driver has promises already (if you want bluebird you can specify in options or as I do attach it to global.Promise) DO NOT DO THIS! – John Culviner Jun 22 '17 at 13:39
2

Here's a one liner to open connection

export const openConnection = async ()  =>
     await MongoClient.connect('mongodb://localhost:27017/staticback')

and call it like this

const login = async () => 
const client = await openConnection()
nick
  • 601
  • 7
  • 7
1

You can either use an alternative package, such as mongodb-promise or promisify the mongodb package API manually by building your own promises around it or via a promise utility package like bluebird.promisify

markusthoemmes
  • 3,080
  • 14
  • 23
  • MongoDB driver has promises already (if you want bluebird you can specify in options or as I do attach it to global.Promise) DO NOT DO THIS! – John Culviner Jun 22 '17 at 13:40
1

Working solution with MongoDB version > 3.0

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";


open = (url) => {
    return new Promise((resolve,reject) => {
        MongoClient.connect(url, (err,client) => { //Use "client" insted of "db" in the new MongoDB version
            if (err) {
                reject(err)
            } else {
                resolve({
                    client
                });
            };
        });
    });
};

create = (client) => {
    return new Promise((resolve,reject) => {
        db = client.db("myFirstCollection"); //Get the "db" variable from "client"
        db.collection("myFirstCollection").insertOne({
            name: 'firstObjectName',
            location: 'London'
            }, (err,result)=> {
                if(err){reject(err)}
                else {
                    resolve({
                        id: result.ops[0]._id, //Add more variables if you want
                        client
                    });
                }

            });
    });
};

close = (client) => {
    return new Promise((resolve,reject) => {
        resolve(client.close());
    })

};

open(url)
    .then((c) => {
        clientvar = c.client;
        return create(clientvar)
    }).then((i) => {
        idvar= i.id;
        console.log('New Object ID:',idvar) // Print the ID of the newly created object
        cvar = i.client
        return close(cvar)
    }).catch((err) => {
        console.log(err)
    })
1

This is based upon @pirateApp's answer.


const open = (dbName, collectionName) => {
  const URI = process.env.MONGO_URI;
  return new Promise((resolve, reject) => {
    let savedConn = null;
    MongoClient.connect(URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    })
      .then((conn) => {
        savedConn = conn;
        return conn.db(dbName).collection(collectionName);
      })
      .then((db) => {
        resolve({ db, savedConn });
      })
      .catch((err) => reject(err));
  });
};

Afroz
  • 13
  • 4
0

You need to create a promise that connects to Mongo.

Then, define your function that uses this promise: myPromise.then(...).

For example:

function getFromMongo(cb) {
    connectingDb.then(function(db) {

       db.collection(coll).find().toArray(function (err,result){
           cb(result);
       });

    });
}

here is the full code:

http://jsfiddle.net/t5hdjejg/

Maria Maldini
  • 473
  • 1
  • 4
  • 7
  • MongoDB driver has promises already (if you want bluebird you can specify in options or as I do attach it to global.Promise) DO NOT DO THIS! – John Culviner Jun 22 '17 at 13:40
  • @JohnCulviner as far as I can tell, .find does not return a promise? Some methods do -- .count() on a cursor does, for example -- but db.mycoll.find({}).then is undefined? – sil Jun 26 '17 at 23:07
  • @sil db.get("collection").find({something:"a"}).then().catch(); works for me – Rafique Mohammed Feb 10 '18 at 10:36
0

async function main(){
 let client, db;
 try{
    client = await MongoClient.connect(mongoUrl, {useNewUrlParser: true});
    db = client.db(dbName);
    let dCollection = db.collection('collectionName');
    let result = await dCollection.find();   
    // let result = await dCollection.countDocuments();
    // your other codes ....
    return result.toArray();
 }
 catch(err){ console.error(err); } // catch any mongo error here
 finally{ client.close(); } // make sure to close your connection after
}
Jaan
  • 1
-1

It doesn't look like the connect method has a promise interface defined

http://mongodb.github.io/node-mongodb-native/2.1/tutorials/connect/

you could always implement it yourself in the Mongodb connector library, but that's probably more involved than you are looking for.

If you really need to work with promises, you can always use the ES6 promise polyfill:

https://github.com/stefanpenner/es6-promise

and wrap your connection code with that. Something like

var MongoClient = require('mongodb').MongoClient;
var Promise = require('es6-promise').Promise;

var url = 'mongodb://localhost:27017/example';

var promise = new Promise(function(resolve, reject){
    MongoClient.connect(url, function (err, db) {
        if(err) reject(err);
        resolve(db);
    });        
});

promise.then(<resolution code>);