0

Currently working on a backend in nodeJS which uses express,socket.io, and has MongoDB as its database. I read that it is a good practice to implement a singleton DB connection which is to be reused in the entire application. I tried to implement this didn't find a solution yet. I tried to use this SO answer of go-oleg.

I replicated the first part, the external mongoUtil.js file like this:

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

var _db;

module.exports = {

  connectToServer: function( callback ) {
    MongoClient.connect( "mongodb://localhost:27017/marankings", function( err, db ) {
      _db = db;
      return callback( err );
    } );
  },

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

Then in my server.js I call this function once like this (I don't do anything with the callback is this required?).

var mongoUtil = require( 'mongoUtil' );

mongoUtil.connectToServer( function( err ) {
  // start the rest of your app here
} );

then when I tried the following in another module of the application:

const db = mongoDB.getDb();

router.post('/', (req, res, next) => {
    console.log(db);
    next();
})

Logs undefined

The log says undefined and thus I dont have a DB instance.

Question:

Why isn't this working and how can I solve this issue?

Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
  • I managed to solve this on a personal project, but I did not know myself if it was a best practice... my code is using ecmascript 6.0 standard... if you're interested... I can put pieces of it... – David Espino May 08 '18 at 17:02
  • 1
    Too many problems. Try some up-to-date **tutorials**. The answer you are referring to assumes some basic experience with node and is unfortunately outdated. [MongoClient.connect](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#~connectCallback) returns client, not db since v3 of the driver. – Alex Blex May 08 '18 at 17:03
  • Yes I would appreaciate that, thanks! – Willem van der Veen May 08 '18 at 17:03

1 Answers1

1

Ok... so, this is could be using a bit of outdated use of Mongo, but this is how I managed to solve the singleton piece of the DB. Disclaimer: it was a personal learning project for Mongo and Ecmascript 6.0 so not sure it follows the Best practices, but it works.

Fist, Db Connection: personnaDB.js

/************  Copyright ************/
/* Year: 2016
 * Author: David Espino
*/
"use strict"
// Imports
const url             = require('url'),
      connectionUrl   = process.env.CONNECTION_STRING || 'mongodb://localhost:27017/personna',
      parsedUrl       = url.parse(connectionUrl),
      Db              = require('mongodb').Db,
      Server          = require('mongodb').Server,
      Connection      = require('mongodb').Connection,
      Q               = require("q"),
      mongoose        = require("mongoose"),
      dao             = require('./personnaDao'),
      autoIncrement   = require( 'mongoose-auto-increment' );



// Symbol Keys
const _connKey = Symbol();
const _connInfoKey = Symbol();
const _monConnKey = Symbol();
const _dataModelsKey = Symbol();
const _autoIncrementKey = Symbol();

/**
 * This class represents the DB Connection
 */
class PersonnaDb {
  /**
   * Class Constructor
   * @return {[type]} [description]
   */
  constructor() {
    let mongoObject = null;
    this[_connInfoKey] = {
      host:     parsedUrl.hostname,
      port:     parseInt(parsedUrl.port, 10),
      name:     parsedUrl.pathname.substr(1),
      user:     parsedUrl.auth ? parsedUrl.auth.split(':')[0] : null,
      password: parsedUrl.auth ? parsedUrl.auth.split(':')[1] : null
    };
    this._connInstance = null;
  }

  /**
   * Opens the DB connection using regular mongo db access
   * @return {[type]} [description]
   */
  openConnection() {
    let deferred = Q.defer();
    if (this[_connKey]) {
      console.log('---> not need to create instance');
      deferred.resolve(this[_connInfoKey]);
    } else {
      let $this = this;
      const mongoObject = new Db('your-db', new Server(this[_connInfoKey].host, this[_connInfoKey].port, { auto_reconnect: true }));
      mongoObject.open(function(error, databaseConnection) {
        if (error) throw new Error(error);
        console.log('---> Succesfully CREATED connection');
        $this[_connKey] = databaseConnection;
        // Initialize auto increment
        autoIncrement.initialize(databaseConnection);
        $this[_autoIncrementKey] = autoIncrement;
        deferred.resolve($this);
      });
    }
    return deferred.promise;
  } 

  /**
   * Opens a Mongo db connection
   * @return {[type]} [description]
   */
  openMongooseConnection() {
    mongoose.connect(connectionUrl); 
    // set the identity plugin
    autoIncrement.initialize(mongoose.connection);
    this[_autoIncrementKey] = autoIncrement;

    // CONNECTION EVENTS
    // When successfully connected
    mongoose.connection.on('connected', function () {  
      console.log('Mongoose default connection open to ' + parsedUrl);
    }); 

    // If the connection throws an error
    mongoose.connection.on('error',function (err) {
      console.log('Mongoose default connection error: ' + err);
    });

    // When the connection is disconnected
    mongoose.connection.on('disconnected', function () {
      console.log('Mongoose default connection disconnected');
    });

    // If the Node process ends, close the Mongoose connection
    process.on('SIGINT', function() {
      mongoose.connection.close(function () {
        console.log('Mongoose default connection disconnected through app termination');
        process.exit(0);
      });
    });

    this[_dataModelsKey] = dao.PersonaDataModels.GetModels(this[_autoIncrementKey]);
   // require('../models/data/bodySectionModel');
  }

  dataModels() {
    return this[_dataModelsKey];
  }
}

module.exports.PersonnaDb = PersonnaDb;

Second... services setup (my layer that takes the dbconnection)

services > include.js

const ModifierService  = require('./modifierService').ModifierService;
const BodySectionService  = require('./bodySectionService').BodySectionService;
module.exports = (dbConnection) => {
  return {
    Modifier: new ModifierService(dbConnection),
    BodySection: new BodySectionService(dbConnection),

  }
}

Sample of the service (it basically intitializes the mongo Model. BodySectionService.js

class BodySectionService extends BaseService {

  constructor(db) {
    super(db.dataModels().BodySection); // this is the mongo model with the schema etc
  }

Then DB initialization (and passing the DB Connection as a singleton object)

app.js

var express = require('express');
const PersonnaDb = require('./dao/personnaDb').PersonnaDb;
const dbConnection = new PersonnaDb();

var app = express();
// Open DB Connection
dbConnection.openMongooseConnection();
// get the services
const services = require('./services/include')(dbConnection);

// // This is the piece that I was not totally sure, making this available on the app
app.set('services', services);
app.set('dbAccess', dbConnection); 

And this is how I use it:

bodySectionController.js

router.get('/', function(req, res, next) {
  const bodySectionProxy = req.app.get("services").BodySection;
  const logger = req.app.get("customLogger");
  var result = bodySectionProxy.getBodySections({}).then((result) => {
    res.json(result);
  })
  .fail((err) => {
    logger.logError(err);
    res.json(err.message);
  })
  .done();
});

module.exports = router;

I've excluded the Path on how I set the models on the service since your question only asks about How to setup the DB. But if you need me to, I could extend the answer with that too.

Hope this gives an idea.

David Espino
  • 2,177
  • 14
  • 21