NitinD
NitinD

Reputation: 499

What is best way to handle global connection of Mongodb in NodeJs

I using Node-Mongo-Native and trying to set a global connection variable, but I am confused between two possible solutions. Can you guys help me out with which one would be the good one? 1. Solution ( which is bad because every request will try to create a new connection.)

var express = require('express');  
var app = express();  
var MongoClient = require('mongodb').MongoClient;  
var assert = require('assert');

// Connection URL
var url = '[connectionString]]';

// start server on port 3000
app.listen(3000, '0.0.0.0', function() {  
  // print a message when the server starts listening
  console.log("server starting");
});

// Use connect method to connect to the server when the page is requested
app.get('/', function(request, response) {  
  MongoClient.connect(url, function(err, db) {
    assert.equal(null, err);
    db.listCollections({}).toArray(function(err, collections) {
        assert.equal(null, err);
        collections.forEach(function(collection) {
            console.log(collection);
        });
        db.close();
    })
    response.send('Connected - see console for a list of available collections');
  });
});
  1. Solution ( to connect at app init and assign the connection string to a global variable). but I believe assigning connection string to a global variable is a not a good idea.

    var mongodb; var url = '[connectionString]'; MongoClient.connect(url, function(err, db) {
    assert.equal(null, err); mongodb=db; } );

I want to create a connection at the app initialization and use throughout the app lifetime.

Can you guys help me out? Thanks.

Upvotes: 26

Views: 19590

Answers (7)

user1063287
user1063287

Reputation: 10839

Here is a version of Matt's answer that lets you define database as well as collection when using the connection. Not sure if it is as 'water tight' as his solution, but it was too long for a comment.

I removed Connection.options as they were giving me errors (perhaps some options are deprecated?).

lib/Connection.js

const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');

class Connection {
  static async open() {
    if (this.conn) return this.conn;
    this.conn = await MongoClient.connect(connection_string);
    return this.conn;
  }
}

Connection.conn = null;
Connection.url = connection_string;

module.exports = { Connection };

testRoute.js

const express = require('express');
const router = express.Router();
const { Connection } = require('../lib/Connection.js');

Connection.open();

router.route('/').get(async (req, res) => {
    try {
        const query = { username: 'my name' };
        const collection = Connection.conn.db('users').collection('users');
        const result = await collection.findOne(query);
        res.json({ result: result });
    } catch (error) {
        console.log(error);
        res.status(500).json({ error });
    }
});

module.exports = router;

If you would like to take the middleware out of the route file:

testRoute.js becomes:

const express = require('express');
const router = express.Router();
const test_middleware_01 = require('../middleware/test_middleware_01');

router.route('/').get(test_middleware_01);

module.exports = router;

And the middleware is defined in middleware/test_middleware_01.js:

const { Connection } = require('../lib/Connection.js');

Connection.open();

const test_middleware_01 = async (req, res) => {
    try {
        const query = { username: 'my name' };
        const collection = Connection.conn.db('users').collection('users');
        const result = await collection.findOne(query);
        res.json({ result: result });
    } catch (error) {
        console.log(error);
        res.status(500).json({ error });
    }
};

module.exports = test_middleware_01;

Upvotes: 0

I did a lot of research on the answer but couldn't find a solution that would convince me so I developed my own.

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

class DB {
    static database;
    static client;

    static async setUp(url) {
        if(!this.client) {
            await this.setClient(url);
            await this.setConnection();
        }

        return this.database;
    }

    static async setConnection() {
        this.database = this.client.db("default");
    }

    static async setClient(url) {
        console.log("Connecting to database");
        const client = new MongoClient(url);

        await client.connect();

        this.client = client;
    }
}

module.exports = DB;

Usage:

const DB = require("./Path/to/DB");
(async () => {
  const database = await DB.setUp();
  const users = await database.collection("users").findOne({ email: "" });
});

Upvotes: 0

Matt
Matt

Reputation: 74620

Create a Connection singleton module to manage the apps database connection.

MongoClient does not provide a singleton connection pool so you don't want to call MongoClient.connect() repeatedly in your app. A singleton class to wrap the mongo client works for most apps I've seen.

const MongoClient = require('mongodb').MongoClient

class Connection {

    static async open() {
        if (this.db) return this.db
        this.db = await MongoClient.connect(this.url, this.options)
        return this.db
    }

}

Connection.db = null
Connection.url = 'mongodb://127.0.0.1:27017/test_db'
Connection.options = {
    bufferMaxEntries:   0,
    reconnectTries:     5000,
    useNewUrlParser:    true,
    useUnifiedTopology: true,
}

module.exports = { Connection }

Everywhere you require('./Connection'), the Connection.open() method will be available, as will the Connection.db property if it has been initialised.

const router = require('express').Router()
const { Connection } = require('../lib/Connection.js')

// This should go in the app/server setup, and waited for.
Connection.open()

router.get('/files', async (req, res) => {
   try {
     const files = await Connection.db.collection('files').find({})
     res.json({ files })
   }
   catch (error) {
     res.status(500).json({ error })
   }
})

module.exports = router

Upvotes: 47

sahad kkt
sahad kkt

Reputation: 3

In Express you can add the mongo connection like this

import {MongoClient} from 'mongodb';
import express from 'express';
import bodyParser from 'body-parser';
    let mongoClient = null;
    MongoClient.connect(config.mongoURL, {useNewUrlParser: true, useUnifiedTopology: true},function (err, client) {
        if(err) {
          console.log('Mongo connection error');
        } else {
          console.log('Connected to mongo DB');
          mongoClient = client;
        }
    })
let app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use((req,res,next)=>{
    req.db = mongoClient.db('customer_support');
    next();
});

and later you can access it as req.db

router.post('/hello',async (req,res,next)=>{
    let uname = req.body.username;
    let userDetails = await getUserDetails(req.db,uname)
    res.statusCode = 200;
    res.data = userDetails;
    next();
});

Upvotes: 0

Rolly
Rolly

Reputation: 3365

This is how i did.

// custom class
const MongoClient = require('mongodb').MongoClient
const credentials = "mongodb://user:pass@mongo"

class MDBConnect {
    static connect (db, collection) {
        return MongoClient.connect(credentials)
            .then( client => {
                return client.db(db).collection(collection);
            })
            .catch( err => { console.log(err)});
    }
    static findOne(db, collection, query) {
        return MDBConnect.connect(db,collection)
            .then(c => {
                return c.findOne(query)
                            .then(result => {
                                return result;
                            });
            })
    }
    // create as many as you want
    //static find(db, collection, query)
    //static insert(db, collection, query)
    // etc etc etc
}
module.exports = MDBConnect;


// in the route file
var express = require('express');
var router = express.Router();
var ObjectId = require('mongodb').ObjectId; 
var MDBConnect =  require('../storage/MDBConnect');

// Usages
router.get('/q/:id', function(req, res, next) {
    let sceneId = req.params.id;
    
    // user case 1
    MDBConnect.connect('gameapp','scene')
        .then(c => {
            c.findOne({_id: ObjectId(sceneId)})
                .then(result => {
                    console.log("result: ",result);
                    res.json(result);
                })
        });
    // user case 2, with query
    MDBConnect.findOne('gameapp','scene',{_id: ObjectId(sceneId)})
        .then(result => {
            res.json(result);
        });
});

Upvotes: 2

Hoppo
Hoppo

Reputation: 1160

Another more straightforward method is to utilise Express's built in feature to share data between routes and modules within your app. There is an object called app.locals. We can attach properties to it and access it from inside our routes. To use it 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, or indeed any other data you wish to share around the modules of you can now be accessed within your routes with req.app.locals 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 creation of any additional modules.

Upvotes: 5

Henry Bothin
Henry Bothin

Reputation: 243

module version ^3.1.8

Initialize the connection as a promise:

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

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

app.post('/insert', (req, res) => {
    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
        })
    })
})  

Upvotes: 1

Related Questions