195

I've been reading and reading and still am confused on what is the best way to share the same database (MongoDb) connection across whole NodeJs app. As I understand connection should be open when app starts and reused between modules. My current idea of the best way is that server.js (main file where everything starts) connects to database and creates object variable that is passed to modules. Once connected this variable will be used by modules code as necessary and this connection stays open. E.g.:

    var MongoClient = require('mongodb').MongoClient;
    var mongo = {}; // this is passed to modules and code

    MongoClient.connect("mongodb://localhost:27017/marankings", function(err, db) {
        if (!err) {
            console.log("We are connected");

            // these tables will be passed to modules as part of mongo object
            mongo.dbUsers = db.collection("users");
            mongo.dbDisciplines = db.collection("disciplines");

            console.log("aaa " + users.getAll()); // displays object and this can be used from inside modules

        } else
            console.log(err);
    });

    var users = new(require("./models/user"))(app, mongo);
    console.log("bbb " + users.getAll()); // not connected at the very first time so displays undefined

then another module models/user looks like that:

Users = function(app, mongo) {

Users.prototype.addUser = function() {
    console.log("add user");
}

Users.prototype.getAll = function() {

    return "all users " + mongo.dbUsers;

    }
}

module.exports = Users;

Now I have horrible feeling that this is wrong so are there any obvious problems with this approach and if so how to make it better?

tereško
  • 58,060
  • 25
  • 98
  • 150
spirytus
  • 10,726
  • 14
  • 61
  • 75
  • The same kind of question I asked a couple of days ago. http://stackoverflow.com/questions/24547357/setting-up-singleton-connection-with-node-js-and-mongo – Salvador Dali Jul 08 '14 at 01:22
  • Check _[mongoist](https://github.com/saintedlama/mongoist)_ driver. It is "_built with async/await in mind_" and allows lazily export connection like `module.exports = mongoist(connectionString);`. (Read about [`connectionString`](https://docs.mongodb.com/manual/reference/connection-string/) in MongoDB Manual.) – Nil Oct 01 '17 at 19:37

25 Answers25

192

You can create a mongoUtil.js module that has functions to both connect to mongo and return a mongo db instance:

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

var _db;

module.exports = {

  connectToServer: function( callback ) {
    MongoClient.connect( url,  { useNewUrlParser: true }, function( err, client ) {
      _db  = client.db('test_db');
      return callback( err );
    } );
  },

  getDb: function() {
    return _db;
  }
};

To use it, you would do this in your app.js:

var mongoUtil = require( 'mongoUtil' );

mongoUtil.connectToServer( function( err, client ) {
  if (err) console.log(err);
  // start the rest of your app here
} );

And then, when you need access to mongo somewhere else, like in another .js file, you can do this:

var mongoUtil = require( 'mongoUtil' );
var db = mongoUtil.getDb();

db.collection( 'users' ).find();

The reason this works is that in node, when modules are require'd, they only get loaded/sourced once so you will only ever end up with one instance of _db and mongoUtil.getDb() will always return that same instance.

Note, code not tested.

codenaugh
  • 857
  • 2
  • 12
  • 27
go-oleg
  • 19,272
  • 3
  • 43
  • 44
  • 7
    Great example! However, I have a question. How would this work when running your app with multiple clusters? Would it spin up another instance of the connection or simply use the existing connection from source? – Farhan Ahmad Feb 09 '15 at 02:59
  • 30
    How would you handle the case when the mongo connection dies in between? All calls to getDb() would fail in that scenario untill the node application is restarted. – Ayan Aug 22 '17 at 12:02
  • 6
    I tried this code but I got null when do mongoUtil.getDb(), I don't know why is that. – Keming Jan 17 '18 at 19:44
  • 5
    @KemingZeng - you need to make sure that all modules that use mongoUtil are imported in `app.js` within the callback function of `connectToServer`. If you `require` them in `app.js` before `_db` is set, then you'll get undefined errors in the other modules. – Mike R Feb 17 '18 at 21:40
  • 2
    As of mongoDB version 4 it should be `var database = mongoUtil.getDb(); database.db().collection( 'users' )`. – Julian Veerkamp Aug 23 '18 at 12:01
  • you may want to update this answer to be compatible with mongo 3.0+ https://stackoverflow.com/questions/47662220/db-collection-is-not-a-function-when-using-mongoclient-v3-0 – codenaugh Apr 22 '19 at 00:59
  • Nevermind... I was able to edit it to make it compatible with current versions. Hopefully the edit gets approved and we will see it soon :) – codenaugh Apr 22 '19 at 01:11
  • @codenaugh - any chance you could share a more newbie friendly version of the code for mongoutils.js? I'm new to javascript and don't understand how connectToServer works exactly. For instance, shouldn't the ```callback``` in ```connectToServer: function(callback)``` have 2 parameters (one for error and the other for a result)? Also, in the MongoClient.connect function, shouldn't ```return callback(err)``` be executed only if there is an error? Also, is the purpose of connectToServer simply to wrap up MongoClient.connect in a function that can be exported for use in other modules/codes? – coder101 Jun 12 '19 at 11:21
  • To be honest, I'm a noob at this myself. I simply edited it for compatibility with the latest mongo. Good questions tho. Hopefully someone can properly answer them. – codenaugh Jun 13 '19 at 17:29
  • @go-oleg: Can you confirm that purpose of ```callback``` in ```connectToServer: function(callback)``` in mongoUtil.js is that when conenctToServer is invoked from app.js, additional code needs to be executed, which is part of the callback function passed as argument to ```connectToServer``` in app.js. For this callback to be executed, ```connectToServer``` needs to be defined with a parameter (```callback```) in mongoUtil.js, and this callback needs to be invoked inside connectToServer in mongoUtil.js (hence the ```return callback(err)``` statement). If incorrect, can you please explain? – coder101 Jun 18 '19 at 19:11
  • Could someone post a typescript version of this? – Syntle Apr 03 '20 at 01:02
  • @Ayan Did you get any answer to your question? – Zain Shaikh Jul 27 '20 at 12:24
  • Hello, I tried following this example but have not been able to make it work. Here is the link to my question https://stackoverflow.com/questions/65556978/how-to-share-mongodb-connection-across-files?noredirect=1 – Elizabeth Jan 05 '21 at 03:09
  • If you'll only query from one database, i'd rather include the default database name in the mongo client uri, and save it on `process.env.DB_URI`. Then, i'd just need to call `client.db()` instead of `client.db('test_db')`. Also, i'd export a method to get a collection, which is probably the most used function: `export function getCollection(name) { return _db.collection(name); }` – Iglesias Leonardo Mar 07 '22 at 19:21
  • 1
    Is this still the best way to it in '22? – Moriarty Sep 01 '22 at 18:53
58

There are many ways this could be tweaked to accept configuration objects in places, but overall it's similar to how you have your code laid out, albeit with more modern JS syntax. Could easily be rewritten to prototypes and callbacks, if that's your requirement.

mongo.js

const { MongoClient } = require('mongodb');
const config = require('./config');
const Users = require('./Users');
const conf = config.get('mongodb');

class MongoBot {
  constructor() {
    const url = `mongodb://${conf.hosts.join(',')}`;

    this.client = new MongoClient(url, conf.opts);
  }
  async init() {
    await this.client.connect();
    console.log('connected');

    this.db = this.client.db(conf.db);
    this.Users = new Users(this.db);
  }
}

module.exports = new MongoBot();

Users.js

class User {
  constructor(db) {
    this.collection = db.collection('users');
  }
  async addUser(user) {
    const newUser = await this.collection.insertOne(user);
    return newUser;
  }
}
module.exports = User;

app.js

const mongo = require('./mongo');

async function start() {
  // other app startup stuff...
  await mongo.init();
  // other app startup stuff...
}
start();

someFile.js

const { Users } = require('./mongo');

async function someFunction(userInfo) {
  const user = await Users.addUser(userInfo);
  return user;
}
EddieDean
  • 1,806
  • 11
  • 13
  • 1
    This is the neatest approach I've come across – KalenGi Apr 19 '19 at 09:28
  • 1
    I realize this answer is almost a year old and I don't really expect more information, but this seems like the approach I would most like to use yet I am having zero luck pulling the destructured Users object out of the mongo file. I have a file very similar to your someFile.js, but line 4 where you call Users.addUser always blows up for me - says that Users is undefined. Is there an obvious piece that I am missing? – Rob E. Feb 22 '20 at 14:53
  • 1
    I ended up [creating a new question](https://stackoverflow.com/questions/60353529/creating-a-db-service-in-an-mvc-express-app) because this is bugging me so much. – Rob E. Feb 22 '20 at 15:09
  • this shouldn't work technically. Require caches the object in the first call. In this case, it will only cache the object returned by the constructor. Calling 'init' later has no effect on what will be returned. So this const { Users } = require('./mongo') should fail as there won't be any 'User' property on the cached result. – beNerd Apr 13 '20 at 21:31
  • require.cache stores a reference to the object, which is shared amongst all files that require that object. Objects that can be mutated by actions from other parts of the program (or even themselves if you use timers). You can test it yourself quickly, but I threw a quick pen together to demo: https://codesandbox.io/s/awesome-water-cexno – EddieDean Apr 23 '20 at 05:42
  • This pattern is called Active Record https://en.wikipedia.org/wiki/Active_record_pattern I would recommend reading about Data mapper pattern before final decision https://en.wikipedia.org/wiki/Data_mapper_pattern – Daniel Nov 04 '20 at 08:34
  • 2
    Is it possible to detect when the connection drops using this method? – Nicholas Smith Nov 25 '20 at 20:07
  • The problem with this is that the connection with the database will stay open all the time. You're suposed to connect only when it's needed to CRUD on the database. Don't forget to `client.close()`. – Iglesias Leonardo Mar 07 '22 at 17:43
  • @NicholasSmith yes, of course, you just add your client event listeners as normal somewhere after client creation – EddieDean Mar 16 '22 at 19:51
  • 1
    @IglesiasLeonardo to quote people smarter than me, "it depends". If you're servicing many thousands of requests per second, it's likely you don't want to wait for a handshake each time and instead will utilize connection pools. – EddieDean Mar 16 '22 at 19:52
  • You're right. MongoDB "tells" people to close connection per request, but it's not a very good approach, especially to serve millions of users. – Iglesias Leonardo Mar 16 '22 at 20:01
  • it's 2022 and it's still the neatest approach i've come across – Sun Lee May 13 '22 at 04:37
  • This doesn't work all the way. for "someFile.js" It doesn't desctructure: const { Users } = require('./mongo'); const Users = require('./mongo'); Users.Users.addUser() Would end up working instead. – Brad Vanderbush Jun 08 '22 at 01:55
22

If you are using Express, then you can use mongo-express-req module that allows you to get db connection in request object.

Install

npm install --save mongo-express-req

server.js

var app = require('express')();

var mongoExpressReq = require('mongo-express-req');
app.use(mongoExpressReq('mongodb://localhost/test'));

routes/users.js

app.get('/', function (req, res, next) {
    req.db // => Db object
});

Note: mongo-express-req is fork of not maintained express-mongo-db.

Daniel
  • 7,684
  • 7
  • 52
  • 76
Mukesh Chapagain
  • 25,063
  • 15
  • 119
  • 120
22

Here's how I do it with contemporary syntax, based on go-oleg's example. Mine is tested and functional.

I put some comments in the code.

./db/mongodb.js

 const MongoClient = require('mongodb').MongoClient
 const uri = 'mongodb://user:password@localhost:27017/dbName'
 let _db

 const connectDB = async (callback) => {
     try {
         MongoClient.connect(uri, (err, db) => {
             _db = db
             return callback(err)
         })
     } catch (e) {
         throw e
     }
 }

 const getDB = () => _db

 const disconnectDB = () => _db.close()

 module.exports = { connectDB, getDB, disconnectDB }

./index.js

 // Load MongoDB utils
 const MongoDB = require('./db/mongodb')
 // Load queries & mutations
 const Users = require('./users')

 // Improve debugging
 process.on('unhandledRejection', (reason, p) => {
     console.log('Unhandled Rejection at:', p, 'reason:', reason)
 })

 const seedUser = {
     name: 'Bob Alice',
     email: 'test@dev.null',
     bonusSetting: true
 }

 // Connect to MongoDB and put server instantiation code inside
 // because we start the connection first
 MongoDB.connectDB(async (err) => {
     if (err) throw err
     // Load db & collections
     const db = MongoDB.getDB()
     const users = db.collection('users')

     try {
         // Run some sample operations
         // and pass users collection into models
         const newUser = await Users.createUser(users, seedUser)
         const listUsers = await Users.getUsers(users)
         const findUser = await Users.findUserById(users, newUser._id)

         console.log('CREATE USER')
         console.log(newUser)
         console.log('GET ALL USERS')
         console.log(listUsers)
         console.log('FIND USER')
         console.log(findUser)
     } catch (e) {
         throw e
     }

     const desired = true
     if (desired) {
         // Use disconnectDB for clean driver disconnect
         MongoDB.disconnectDB()
         process.exit(0)
     }
     // Server code anywhere above here inside connectDB()
 })

./users/index.js

 const ObjectID = require('mongodb').ObjectID

 // Notice how the users collection is passed into the models
 const createUser = async (users, user) => {
     try {
         const results = await users.insertOne(user)
         return results.ops[0]
     } catch (e) {
         throw e
     }
 }

 const getUsers = async (users) => {
     try {
         const results = await users.find().toArray()
         return results
     } catch (e) {
         throw e
     }
 }

 const findUserById = async (users, id) => {
     try {
         if (!ObjectID.isValid(id)) throw 'Invalid MongoDB ID.'
         const results = await users.findOne(ObjectID(id))
         return results
     } catch (e) {
         throw e
     }
 }

 // Export garbage as methods on the Users object
 module.exports = { createUser, getUsers, findUserById }
agm1984
  • 15,500
  • 6
  • 89
  • 113
  • is the try catch in your first snippet necessary? the connect function is an async function. The error is already being caught using the node style callback. – shanks May 16 '18 at 21:44
  • 1
    It's a very observant question that I love. I'm not sure without studying it closer in the habitat you place the code. There will be a limited number of pathways it could take during code execution. I added it mostly to show that you could put a custom handler there and because I default to include try/catch in async functions. It is simply a hook point. Good question though. I will update if you find an additional note. – agm1984 May 17 '18 at 22:08
  • every time if i call getDB() it will create new connections, right? – Vinay Pandya Apr 05 '19 at 07:13
13

A tested solution based on the accepted answer:

mongodbutil.js:

var MongoClient = require( 'mongodb' ).MongoClient;
var _db;
module.exports = {
  connectToServer: function( callback ) {
    MongoClient.connect( "<connection string>", function( err, client ) {
      _db = client.db("<database name>");
      return callback( err );
    } );
  },
  getDb: function() {
    return _db;
  }
};

app.js:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

var mongodbutil = require( './mongodbutil' );
mongodbutil.connectToServer( function( err ) {
  //app goes online once this callback occurs
  var indexRouter = require('./routes/index');
  var usersRouter = require('./routes/users');
  var companiesRouter = require('./routes/companies');
  var activitiesRouter = require('./routes/activities');
  var registerRouter = require('./routes/register');  
  app.use('/', indexRouter);
  app.use('/users', usersRouter);
  app.use('/companies', companiesRouter);
  app.use('/activities', activitiesRouter);
  app.use('/register', registerRouter);  
  // catch 404 and forward to error handler
  app.use(function(req, res, next) {
    next(createError(404));
  });
  // error handler
  app.use(function(err, req, res, next) {
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};
    res.status(err.status || 500);
    res.render('error');
  });
  //end of calback
});

module.exports = app;

activities.js -- a route:

var express = require('express');
var router = express.Router();
var mongodbutil = require( '../mongodbutil' );
var db = mongodbutil.getDb();

router.get('/', (req, res, next) => {  
    db.collection('activities').find().toArray((err, results) => {
        if (err) return console.log(err)
            res.render('activities', {activities: results, title: "Activities"})
    });
});

router.post('/', (req, res) => {
  db.collection('activities').save(req.body, (err, result) => {
    if (err) return console.log(err)
    res.redirect('/activities')
  })
});

module.exports = router;
AndyW
  • 482
  • 7
  • 19
steve
  • 196
  • 1
  • 5
11

Here is my setup in 2020:

./utils/database.js

const { MongoClient } = require('mongodb');

class Mongo {
    constructor () {
        this.client = new MongoClient("mongodb://127.0.0.1:27017/my-app", {
            useNewUrlParser: true,
            useUnifiedTopology: true
        });
    }

    async main () {
        await this.client.connect();
        console.log('Connected to MongoDB');

        this.db = this.client.db();
    }
}

module.exports = new Mongo();

/app.js

const mongo = require('./utils/database');
const express = require('express');

const app = express();

const boot = async () => {
    await mongo.main();
    app.listen(3000);
};

boot();
Aditya Hajare
  • 1,372
  • 1
  • 15
  • 24
9

go-oleg is basically right, but in these days you (probably) dont want use "mongodb" itself, rather use some framework, which will do a lot of "dirty work" for you.

For example, mongoose is one of the most common. This is what we have in our initial server.js file :

const mongoose = require('mongoose');
const options = {server: {socketOptions: {keepAlive: 1}}};
mongoose.connect(config.db, options);

This is everything what is needed to set it up. Now use this anywhere in your code

const mongoose = require('mongoose');

And you get that instance you set up with mongoose.connect

libik
  • 22,239
  • 9
  • 44
  • 87
  • 3
    mongoose is an ORM. Read [this](https://medium.com/@ameykpatil/why-orm-shouldnt-be-your-best-bet-fffb66314b1b) to know about possible pitfalls for the same. No doubt ORM's are great when used for developmental and learning process but not for production. Just keep this in mind – Saras Arya Apr 13 '17 at 14:16
  • 1
    Mongoose also requires schemas. I am using MongoDB package as part of polyglot persistence with Neo4j, so it's nice to define document properties as needed. – agm1984 Jul 24 '17 at 06:10
7

I´m late to the party, but hopefully this answer will help someone, this is a functional code:

db.js

const MongoClient = require("mongodb").MongoClient
const urlMongo = "mongodb://localhost:27017"

var db;

function connectToServer( callback ) {
    MongoClient.connect(urlMongo,  { useUnifiedTopology: true , useNewUrlParser: true }, function( err, client ) {
        db  = client.db('auth');
        return callback( err );
    })
}

function getDb() {
    return db
}

module.exports = {connectToServer, getDb}

We export one function to connect to the mongo and another to get de instanceof the connection.

app.js

const express = require('express')
const app = express()

const mongo = require('./db.js');

mongo.connectToServer( function( err) {
  if (err) console.log(err);
  const auth = require('./modulos')

  app.post('/login', (req, res) => { auth.login(req, res)})
  app.listen(3000, function () { console.log('Corriendo en puerto 3000')})

});

We must do the require of the auth module after we initiallize the connection, otherwise the getDb function will return undefined.

module.js

const db = require('../db.js').getDb()
const usuariosCollection = db.collection('usuarios')

function login(req, res){
    usuariosCollection.find({ 'username': 'Fran' }).toArray(function (err, doc) {
        ...
    })
}
Community
  • 1
  • 1
Knemay
  • 350
  • 1
  • 6
  • 12
7

As this is tagged with Express, I thought I would mention that Express has a built in feature to share data between routes. There is an object called app.locals. We can attach properties to it and access it from inside our routes. You simply instantiate your mongo connection in your app.js file.

var app = express();

MongoClient.connect('mongodb://localhost:27017/')
.then(client =>{
  const db = client.db('your-db');
  const collection = db.collection('your-collection');
  app.locals.collection = collection;
});
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              // view engine setup
app.set('views', path.join(__dirname, 'views'));

This database connection can now be accessed within your routes as below without the need for creating and requiring additional modules.

app.get('/', (req, res) => {
  const collection = req.app.locals.collection;
  collection.find({}).toArray()
  .then(response => res.status(200).json(response))
  .catch(error => console.error(error));
});

This method ensures that you have a database connection open for the duration of your app unless you choose to close it at any time. It's easily accessible with req.app.locals.your-collection and doesn't require additional modules.

Hoppo
  • 1,130
  • 1
  • 13
  • 32
  • I find it the cleanest approach. Do we have any possible drawbacks for this approach? I'm using it and looks pretty good to me, would share my learnings. – Priya Ranjan Singh Jul 02 '20 at 16:43
  • 1
    @PriyaRanjanSingh To be honest, I do not know of any drawbacks, but I am by no means an expert on this. I discovered this method after researching as I found the other methods unweildly and I was after cleaner more understandable code for my own benefit. Hopefully someone more knowledgeable than myself will be able to highlight if there are any drawbacks.I have been using this method without any issue though for a while now and it seems to work well. – Hoppo Jul 04 '20 at 10:21
  • 1
    This is actually wonderful, I have been using it since a while now after I learnt from you. One thing I can think in case of larger scale is if we have multiple instances of app running behind pm2/forever, it won't be shared. For now, this is perfect :) – Priya Ranjan Singh Aug 10 '20 at 15:29
  • `app.set('views', path.join(__dirname, 'views'));` this line is **unnecessary** right? – fuat Mar 18 '21 at 12:48
  • @fuat That is part of the boiler plate code which is in the original app.js file created with express. I included it to show where the code is inserted in the app.js file. – Hoppo Apr 01 '21 at 18:22
5

Initialize the connection as a promise:

const MongoClient = require('mongodb').MongoClient
const uri = 'mongodb://...'
const client = new MongoClient(uri)
const connection = client.connect() // initialized connection

And then call the connection whenever you wish you perform an action on the database:

    // if I want to insert into the database...
    const connect = connection
    connect.then(() => {
        const doc = { id: 3 }
        const db = client.db('database_name')
        const coll = db.collection('collection_name')
        coll.insertOne(doc, (err, result) => {
            if(err) throw err
        })
    })
Henry Bothin
  • 243
  • 3
  • 6
  • 1
    how to use the same single connection across different routes and middleware (without importing `connection ` into each module)? – user1063287 Nov 27 '21 at 09:58
5

Here's a suggestion using TypeScript and ES6 features and syntax:

db.ts

import { Db, MongoClient } from 'mongodb'

let client: MongoClient
let db: Db

const connectToDatabase = async () => {
    client = new MongoClient('databaseURI')
    await client.connect()
    db = client.db('dbname')
}

export {
    connectToDatabase,
    client,
    db,
}

index.ts

import express from 'express'
import { someRouter } from './routes/someRoute'
import { connectToDatabase } from './db'

connectToDatabase().then(() => {
    const app = express()
    app.use('/someRoute', someRouter)
    const port = process.env.PORT || 5000
    app.listen(port, () => {
        console.log(`Server is listening on port ${port}`)
    })
})

routes/someRoute.ts

import express from 'express'
import { db } from '../db'

const someRouter = express.Router()

someRouter.route('/')
    .get(async (req, res) => {
        const results = await db.collection('collectionName').find().toArray()
        return res.send(results)
    })

export { 
    someRouter,
}
rdhelms
  • 91
  • 1
  • 6
3

we can create a dbconnection file like dbconnection.js

const MongoClient = require('mongodb').MongoClient
const mongo_url = process.env.MONGO_URL;

    module.exports = {
        connect: async function(callback) {
            var connection;
            await new Promise((resolve, reject) => {
                MongoClient.connect(mongo_url, {
                    useNewUrlParser: true
                }, (err, database) => {
                    if (err)
                        reject();
                    else {
                        connection = database;
                        resolve();
                    }
                });
            });
            return connection;
        }

    };

and then use this file in the your app like

var connection = require('../dbconnection');

and then use like this inside your async function

db  = await connection.connect();

hope this will work

gaurav
  • 309
  • 2
  • 7
3

I find this works well :)

mongoUtil.ts

import { MongoClient } from 'mongodb';
const uri =
  'MONGOSTRING';

let connPoolPromise: any = null;

const mongoPoolPromise = () => {
  if (connPoolPromise) return connPoolPromise;

  connPoolPromise = new Promise((resolve, reject) => {
    const conn = new MongoClient(uri, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });

    if (conn.isConnected()) {
      return resolve(conn);
    } else {
      conn
        .connect()
        .then(() => {
          return resolve(conn.db('DATABASENAME'));
        })
        .catch(err => {
          console.log(err);
          reject(err);
        });
    }
  });

  return connPoolPromise;
};

export = {
  mongoPoolPromise,
};

anyFile.ts

const { mongoPoolPromise } = require('./mongoUtil');

async function getProducts() {
  const db = await mongoPoolPromise();
  const data = await db
    .collection('myCollection')
    .find({})
    .toArray();
  console.log(data);
  return data;
}

export { getProducts };
Adam91Holt
  • 1,018
  • 1
  • 14
  • 28
2

I'm a bit late for this, but I'll add my solution too. It's a much noobier approach compared to the answers here.

Anyway if you are using MongoDB version 4.0 and Node.js 3.0 (or higher versions) you can use isConnected() function from the MongoClient.

const MongoClient = require('mongodb').MongoClient;
const uri = "<your connection url>";
const client = new MongoClient(uri, { useNewUrlParser: true });

if (client.isConnected()) {
  execute();
} else {
  client.connect().then(function () {
    execute();
  });
}

function execute() {
    // Do anything here
    // Ex: client.db("mydb").collection("mycol");
}

This worked fine for me. Hope it helps.

Roshana Pitigala
  • 8,437
  • 8
  • 49
  • 80
2

Based on accepted answers, I use a simple approach. But use this only if you want to use db inside function which will be executed after some time. For ex: In express route functions, it is the easiest approach you can take.

mongo.js

const MongoClient = require("mongodb").MongoClient

var db

const connectDb = (callback) => {
    if (db) return callback()
    MongoClient.connect( uri, {ops}, 
        (err, database) => {
            if (err) return console.log(err)
            db = database.db("dbName") 
            console.log("Database Connected")
            callback()
        }
    )
}

const getDb = (collectionToGet) => {
    return db.collection(collectionToGet)
}

module.exports = {
    connectDb,
    getDb,
}

Now, in other files where you want the db object,

user.js

const { connectDb, getDb } = require('mongo.js')

var db // store db object in this object
connectDb(() => ( db = getDb("user") ))

app.get('/', (req, res) => {
    // do something with req 
    db.insert({})
    // do something with res
}
Umang Patel
  • 31
  • 1
  • 5
2

Folks, in 2022 there is no need for reconnection logic, the Node.js MongoDB driver handles this all for you (v4+).

You can simply connect as described in the official docs. Put this in a db.js file, then you can import client or db anywhere in your app:

import { MongoClient, ServerApiVersion } from 'mongodb'
const uri = `mongodb+srv://...`;

// Create a new MongoClient
export const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 });

export const db = client.db('your_db');

When making queries, use try/catch to capture potential connection errors.

try {
    const res = await db.collection("testdata").insertOne({test: Math.random()});
    console.log('Inserted', res);
} catch(e) {
    console.error('MONGO ERROR', e);
}

AFAIK, the Mongo driver will keep retrying forever if the connection is lost.

Try it yourself: put the above code in a setInterval and turn off your internet connection for a while then turn it back on, Mongo will automatically reconnect, even after hours of downtime. It will even submit some queries that were made while the connection was down.

MongoMan
  • 21
  • 2
1

If you opt for using mongoose in your application edit your app.js file with the following snippet

app.js

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/Your_Data_Base_Name', {useNewUrlParser:true})
  .then((res) => {
    console.log(' ########### Connected to mongDB ###########');
  })
  .catch((err) => {
    console.log('Error in connecting to mongoDb' + err);
  });`

Next Step: Define Models for your application require them and perform CRUD operation directly for example

blogSchema.js

 const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 const blogSchema = new Schema({
     _id : mongoose.Schema.Types.ObjectId,
     title : {
        type : 'String',
        unique : true,
        required : true       
    },
    description : String,
        comments : [{type : mongoose.Schema.Types.ObjectId, ref: 'Comment'}]
 });
 module.exports = mongoose.model('Blog', blogSchema);

Usage createBlog.js

const Blog = require('../models/blogSchema');
exports.createBlog = (req, res, next) => {
const blog = new Blog({
  _id : new mongoose.Types.ObjectId,
  title : req.body.title,
  description : req.body.description,
});
blog.save((err, blog) => {
  if(err){
    console.log('Server Error save fun failed');
    res.status(500).json({
      msg : "Error occured on server side",
      err : err
    })
  }else{
    //do something....
  }

U don't need to connect to mogoDB always ....

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

module.exports = {
    DBConnection:async function()
    {
        Pro1 = new Promise(async function(resolve,reject){
            MongoClient.connect(url, { useNewUrlParser: true },function(err, db) {
                if (err) throw err;
                resolve(db);
            });        
        });
    },
    getDB:async function(Blockchain , Context)
    {
        bc = Blockchain;
        contx = Context;
        Pro1.then(function(_db)
        {
            var dbo = _db.db('dbname');
            dbo.collection('collectionname').find().limit(1).skip(0).toArray(function(err,result) {
                if (err) throw err;
                console.log(result);
            });
        });
    },
    closeDB:async function()
    {
        Pro1.then(function(_db){
            _db.close();
        });
    }
};
Tejas Naik
  • 19
  • 1
1
const express = require('express')
const server = express()
const mongoClient = require('./MongoDB.js').client
const port = 3000
;(async () => {
    await mongoClient.connect()
    server.listen(port, () => console.log(`Server is listening on port ${port}!`))
})().catch(console.error)
Xulong Zhang
  • 107
  • 2
  • 6
1

You can use the Singleton Design Pattern to achive cross file usage of your MongoDB connection.

Init.mjs

/* ################ Controller ################ */
import ctrlLib from '../../controller/lib.mjs';

/* ################ MongoDB ################ */
import mongodb from 'mongodb';

/* ################ Logs ################ */
import log from 'fancy-log';
import chalk from 'chalk';


/** Init MongoDB connection */
export class Init {
  /**
   * Check if its first time usage of this class.
   If true set class instance to this that we always get same instance.
   * Then get MongoDB details from config.yml and set as global.
   * In the last step we return the class instance.
  */
  constructor() {
    if (Init.instance == null) Init.instance = this;

    const config = ctrlLib.getConfig();
    this.MongoURL = config.MongoDB.url;
    this.MongoName = config.MongoDB.dbname;
    ({MongoClient: this.MongoClient} = mongodb);

    return Init.instance;
  }; // constructor(){


  /** Connect to Database and return connection */
  async connect() {
    try {
      const client = await this.MongoClient.connect(
          this.MongoURL, {useNewUrlParser: true, useUnifiedTopology: true},
      );

      this.connection = {'db': client.db(this.MongoName), 'client': client};
      return this.connection;
    } // try {
    catch (e) {
      log( `${chalk.red.bold('❌ ERROR')} while try to connect to MongoDB DB
      ${chalk.white.bold('Error:\n')} ${e}` );
    } // catch (e) {
  }; // async connect() {


  /**
   * Return connection for cross file usage
   * @return {object}
  */
  getConnection() {return this.connection;};
}; // export class Init {

app.mjs

  • Make sure to 1x time create your MongoDB connection anywhere inside of your project that you can use it later in other files.
/* ################ Services ################ */
import {Init} from './Init.mjs';

(async ()=>{
  await new Init().connect();
})().catch(e=>{log('app.mjs - Catch error: ' + e);});

anyOtherFile.mjs

/* ################ Services ################ */
import {Init} from './Init.mjs';

/** Subclass of Search which contains lib functions */
class Lib {
  /**
   * Find data by using search query and return result.
   * @param {string} collection - Name of collection
   * @param {object} query - Search query
  */
  async findOne(collection, query) {
    const connection = new Init().getConnection();
    return await connection.db.collection(collection).findOne(query);
  }; // async findOne() {
}; // class Lib {

CyberT33N
  • 78
  • 5
1

Updated for 2022 MongoClient new updates

MongoUtil.js (For database connection and return database instance)

const { MongoClient } = require('mongodb');
const uri = "your database connection url";
var _db;

module.exports = {
    connectToServer: function (callback) {
        MongoClient.connect(uri, { useNewUrlParser: true }, function (err, client) {
            _db = client.db('testdb');
            return callback(err);
        });
    },

    getDb: function () { //this returns database instance
        return _db;
    }
};

app.js (You can use in any routes or js by importing mongoUtil)

var mongoUtil = require('./mongoUtil');

mongoUtil.connectToServer(function (err, client) {
    if (err) console.log(err);
    console.log(`server is running`);

    insertData(); //or do functions and db queries in any js

});

async function insertData() { //Functions should be async
    var database = mongoUtil.getDb();
    var movies = database.collection('movies');

    const doc = {
        title: "Movie title",
        content: "Movie content",
    }

    const result = await movies.insertOne(doc);
    console.log(`A document was inserted with the _id: ${result.insertedId}`);    
}
Dinith Rukshan Kumara
  • 638
  • 2
  • 10
  • 19
1

Updated for 2023

MongoDB Connection

const { MongoClient, ServerApiVersion } = require('mongodb');
const dbconfig = require('./config');

module.exports = {
    client: client = new MongoClient(dbconfig.uri, { useNewUrlParser: true, useUnifiedTopology: true, serverApi: ServerApiVersion.v1 }),
    connectToDB: async () => {
        try {
            await client.connect()
            console.log('connected!')
        } catch (err) {
            console.log('Err', err)
        }

    }
}

In your controller

const db = require('../config/mongodb.connection')
const hha_data = db.client.db('hha-sit').collection('hnh-data')

exports.addNewCustomer = async (req, res) => {
    try {
        await db.connectToDB()
        let result = await hha_data.findOne({}, { account_id: 'MDB829001337' })
        console.log('result', result)
    } catch (err) {
        console.error('Connection Error !', err)
    } finally {
        await db.client.close()
    }

    res.send('Hi')
}

Please feel free to revise it if you have any suggestions. :)

John Nico Novero
  • 539
  • 5
  • 10
1

This approach is correct, and it can be improved in the following ways:

1.Wrap the MongoClient connect function inside a module and export it as a singleton object to be used across your application. This way, you can make sure only one connection is established to the MongoDB server and is reused across your modules.

2.Add error handling to your code to handle potential issues like a connection failure.

3.Use the MongoDB native driver's connection pooling feature instead of maintaining a single connection throughout the application's lifetime, as this can lead to resource exhaustion and poor performance.

This is an example of a improved implementation:

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

let _db;

const connectToDb = async (url) => {
    if (db) return db;
    let client;
    try {
        client = await MongoClient.connect(url, {
            useNewUrlParser: true,
            useUnifiedTopology: true
        });
        _db = client.db();
    } catch (err) {
        console.error('Error connecting to MongoDB: ', err);
        process.exit(1);
    }
    return _db;
};

module.exports = connectToDb;

const connectToDb = require('./db');

const userModule = async (app) => {
    const db = await connectToDb('mongodb://localhost:27017/marankings');

    return {
        addUser: () => console.log('add user'),
        getAll: () => 'all users'
    };
};

module.exports = userModule;

const userModule = require('./userModule');

(async () => {
    const users = await userModule();
    console.log(users.getAll());
})();
sanket
  • 11
  • 2
0

I tried @go-oleg answer and it works pretty well. Inside getDb() , I make sure _db must be defined. And if not defined, I call the connectToServer() so that it will get defined again. After this I don't have to call connectToServer() in the app.js which makes my code clean.

let getDb = async() => {
    if(_db) {
        return _db
    } else {
        _db = await connectToServer()
        return _db
    }
}

And then, I simply call getDb() everywhere. Also, What I observed, It takes about 64ms on first call. After first call it takes about, 2-6ms everytime.

I answered here because i have less reputation to comment.

Alok Prakash
  • 191
  • 1
  • 9
0

all after long effort my working by this operational method: Please follow this link this is also good solution: https://mrvautin.com/re-use-mongodb-database-connection-in-routes/

Waqas Ali
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 19 '22 at 12:57
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/31325018) – almac777 Mar 22 '22 at 14:02
  • link is dead now.. – Eggcellentos May 29 '23 at 06:59