3

I am currently working on building a backend using Express and MongoDB Native Node. I have been researching attempting to find the best "best practice" for managing connections to a Mongo database and using that connection across the app. My current solution is working and I get desired results via tests in Postman. I have come up with this after being unable to find concrete answers on handling the connections with MongoDB 3.x (without Mongoose) and still modular.

Would someone be willing to give some feedback on my current solution?

My main concern would be that this setup would not be performant. I suspect it may not be, potentially due to opening/closing connections frequently, but I am not sure if the way I am doing it is good or bad practice.

I created a db.js file to serve my connection:

const assert = require("assert");
const MongoClient = require("mongodb").MongoClient;
const base = process.env.PWD;
const config = require(base + "/config");
let db;
let client;


const connect = async () => {
  const url = config.url
  const dbName = config.dbName
  client = await MongoClient.connect(url)
  db = await client.db(dbName)
  return db
}

const disconnect = () => {
  client.close()
}

module.exports = {
  connect: connect,
  disconnect: disconnect
  }

I then set the routes for my 'todos' in index.js inside of my todos folder. Following a best practice suggestion to have all component files in their own folders(open to feedback on folder structure):

const express = require('express'),
base = process.env.PWD,
router = express.Router(),
todos = require(base + '/todos/todosController')

/* GET All Todos */
router.get('/all', todos.getTodos)
/* GET One Todo */
router.get('/todo/:id', todos.getTodo)
/* POST One Todo */
router.post('/todo/:id', todos.addTodo)
/* DELETE One Todo */
router.delete('/todo/:id', todos.deleteTodo)

module.exports = router;

Lastly the actual todosController.js which requires db.js This is where I suspect some improvement could happen but I am just not sure. I connect within the routes via async function and await the connection and assign it to db I do my CRUD queries (all currently working properly) and then disconnect at the end. If this is considered performant and a good practice I am happy with that answer but if there is a way to do this better with current driver and syntax I would be happy for any feedback.

'use strict';

const base = process.env.PWD,
      client = require(base + '/db.js'),
      assert = require('assert')

let db

const getTodos = async (req, res) => {
  db = await client.connect()
  const collection = await db.collection('documents')
  // Find all todos
  collection.find({}).toArray((err, todos) => {
    assert.equal(err, null)
    res.status(200).json(todos)
  })
  client.disconnect()
}
  • I think my answer to a similar question could be useful: https://stackoverflow.com/questions/38485575/best-way-to-connect-to-mongodb-using-node/38493142#38493142 – kevinadi Nov 08 '18 at 02:59

1 Answers1

3

It seems that this is a common misconception that opening and closing a connection on every request is more efficient. Opening connection is expensive and this is one of the reasons for the existence of the connection pools. MongoDB supports those and you should consider them.

Here is an article on the subject of Express/MongoDB connection handling which starts right away with:

A common mistake developers make when connecting to the database is to call MongoClient.connect() in every route handler to get a database connection.

Akrion
  • 18,117
  • 1
  • 34
  • 54
  • I like this solution but when attempting to implement I am running into some issues. In the effort of keeping things modular my project currently uses the [bin/www](https://expressjs.com/en/starter/generator.html) approach for the server. I have a route file that requires the controller with the db queries. Using this approach I can make everything work if I restrict the usage to just app.js(or www) and a single file for the routes and their functions, to avoid requiring the dbs as suggested by that article. This seems to make files bulky and less modular, are there downsides to this approach? – Christopher W Nov 08 '18 at 17:07
  • 1
    The main point of this approach is to have your db connection already established and passed via `require`. It ends up behaving almost like a `singleton` since node would not require again if it already got that file once. It is the opposite of `bulky` really since you now have the freedom to ask for it once and use it anywhere with the only price being the single `require(db)`. – Akrion Nov 08 '18 at 17:16
  • Oh!! That makes much more sense! I really appreciate you sharing your initial answer and the follow-up. – Christopher W Nov 08 '18 at 18:41