I'm trying to create a mongoClient connection pool module to use in my project. The problem is the first time I load the page and the mongoFactory is called, it seems that my mongoConnection is not established yet. Every other time after that I load the page it works just fine. Not sure what i'm missing here..
Here's my mongoFactory.js
/**
* Creates and manages the Mongo connection pool
*
* @type {exports}
*/
var constants = require('../js/constants.js');
var Q = require('q');
var MongoClient = require('mongodb').MongoClient;
var db = null;
var getDb = function() {
console.log('db = '+db);
// If the connection pool has not been created or has been closed, create it, else return an existing connection
if (db === null) {
var def = Q.defer();
// Initialize connection once
MongoClient.connect(constants.mongoUrl, function (err, database) {
if (err) {
def.reject(err);
throw err;
}
console.log('inside connection');
db = database;
def.resolve();
});
def.promise.then(function () {
console.log('before returning connection');
return db;
});
} else {
console.log('connection already exists. returning');
return db;
}
}
module.exports.getDb = getDb;
Here's my file that i'm calling the mongoFactory
var mongoFactory = require('../../lib/mongoFactory');
module.exports = function() {
return {
find: function find(callback) {
var db = mongoFactory.getDb();
var cases = db.collection('mycollection'); // <-- First time i load the page, errors b/c db is undefined. Every other time after that it works just fine. ERROR: TypeError: Uncaught error: Cannot call method 'collection' of undefined
...
Your mongoFactory.getDb
should return a promise which will be fulfilled once the connection is established (which the first time around will take some time and after that will happen immediately).
/**
* Creates and manages the Mongo connection pool
*
* @type {exports}
*/
var constants = require('../js/constants.js');
var Q = require('q');
var MongoClient = require('mongodb').MongoClient;
var db = null;
var getDb = function() {
var def = Q.defer();
console.log('db = '+db);
// If the connection pool has not been created or has been closed, create it, else return an existing connection
if (db === null) {
// Initialize connection once
MongoClient.connect(constants.mongoUrl, function (err, database) {
if (err) {
def.reject(err);
}
console.log('inside connection');
db = database;
def.resolve(db);
});
} else {
def.resolve(db);
}
return def.promise;
}
module.exports.getDb = getDb;
and to use:
var mongoFactory = require( './mongoFactory' );
var con = mongoFactory.getDb();
con.done( function(db) {
// use db here
} );
con.fail( function(err) {
// error occurred
} );
You may want to checkout mongoskin which is a promise wrapper around node-mongodb-native.
Constructing a deferred inside a function doesn't automatically make it blocking - in fact it can't, promises are still asynchronous.
Your function does indeed return nothing (i.e. undefined
) in the case that db === null
, and only returns the database connection if it already has been established from a previous call. Instead, it should always return a Promise which represents the (possibly later arriving) eventual connection.
/**
* Creates and manages the Mongo connection pool
*
* @type {exports}
*/
var constants = require('../js/constants.js');
var Q = require('q');
var MongoClient = require('mongodb').MongoClient;
var dbPromise = null;
module.exports.getDb = function getDb() {
// If the connection pool has been created or is being created, return the existing promise for that, else create it and return the promise for that
if (dbPromise === null) {
var def = Q.defer();
// Initialize connection once
MongoClient.connect(constants.mongoUrl, function (err, database) {
if (err)
def.reject(err);
else
def.resolve(database); // let the database be the result value of the promise!
});
return dbPromise = def.promise;
} else {
return dbPromise;
}
};
Now that your function returns a promise, you will need to use it inside the then
callback:
find: function find(callback) {
return mongoFactory.getDb().then(function(db) {
// db is always available in the promise callback
var cases = db.collection('mycollection');
// return a promise or a value from the callback
}); // and find will return a promise for that value
}
MongoClient.connect
is async in nature, why not doing it the nodejs way? I would change your getDb
signature to accept a callback:
var getDb = function(callback) {
console.log('db = '+db);
// If the connection pool has not been created or has been closed, create it, else return an existing connection
if (db === null) {
// Initialize connection once
MongoClient.connect(constants.mongoUrl, function (err, database) {
if (err) {
callback(err);
return;
}
console.log('inside connection');
db = database;
callback(null, db);
});
} else {
console.log('connection already exists. returning');
callback(null, db);
}
}
Then your consumer must:
mongoFactory.getDb(function(err, db) {
if (err) {
// handle error
}
var cases = db.collection('mycollection');
});