How To Export An Object That Only Becomes Available In An Async Callback?
Solution 1:
The best option, as suggested in the comments by elclanrs, is to export a promise:
// database.jsvarMongoClient = require('mongodb').MongoClient,
Q = require('q'),
connect = Q.nbind(MongoClient.connect, MongoClient);
var promise = connect(/* url */);
module.exports = {
connect: function () {
return promise;
}
}
// app.jsvar database = require('./database');
database.connect()
.then(function (db) {
app.get('/', function (req, res) {
db.collection(/* … */);
});
})
.catch(function (err) {
console.log('Error connecting to DB:', err);
})
.done();
(I'm using awesome Q library here.)
Below's the old version of my answer, left for the sake of history (but if you don't want to use promises, instead of going that road, you should use Matt's answer).
Its downside is that it will open a connection each time you require('database.js)
(bummer!)
// DO NOT USE: left for the sake of history// database.jsvarMongoClient = require('mongodb').MongoClient;
functionconnect(cb) {
MongoClient.connect(/* the URL */, cb);
}
module.exports = {
connect: connect
}
// app.jsvar database = require('./database');
database.connect(function (err, db) {
app.get('/', function (req, res) {
db.collection(/* … */);
});
});
Solution 2:
You can't do it as you want to do it, because, quoting the docs:
Note that assignment to
module.exports
must be done immediately. It cannot be done in any callbacks.
Instead however, you can assign a property of module.exports
in a callback, therefore this will work;
// db.jsrequire('mongodb').MongoClient.connect(/* the URL */, function (err, db) {
module.exports.instance = db;
});
// app.jsvar db = require('./db');
// some time later (when `.instance` is available)
app.get('/', function (req, res) {
db.instance.collection(/* … */);
});
However, the some time later is a bit of a pain, so you may just want to use some sort of callback;
// db.jsvar queue = [];
var instance = null;
require('mongodb').MongoClient.connect(/* the URL */, function (err, db) {
instance = db;
while (queue.length) {
queue.pop()(instance);
}
});
module.exports.done = function (callback) {
if (instance === null) {
queue.push(callback);
} else {
callback(instance);
}
};
// app.jsrequire('./db').done(function (db) {
app.get('/', function (req, res) {
db.collection(/* … */);
});
});
The above also handles cases where handlers via done()
are attached after the connection has already been made.
Solution 3:
The servers typically have 3 phases: init, serve and uninit. This seems obvious but when you start writing servers from scratch (ie. in Java you start inheriting from HttpServlet) sometimes you forget how to do the things...
In the startup phase you must open the db connection (pool) and save the object somewhere (typically in your db.js
module). Then in the service phase retrieve the mongodb connection from db.js
.
Related: How to get a instance of db from node-mongo native driver?
Solution 4:
In your code:
// db.jsrequire('mongodb').MongoClient.connect(/* the URL */, function (err, db) {
module.exports = db;
});
// app.jsvar db = require('./db');
app.get('/', function (req, res) {
db.collection(/* … */); // throws error
});
You've called connect
in the db.js
class, yet it's asynchronous.
The call in app.js
to require
is synchronous in behavior though, so it will always receive an undefined value (as the exports
will not be assigned to a value at the time the db.js
has finished executing).
I'd suggest keeping things simple.
The option I usually use is something where the app code makes the connection and doesn't start listening for HTTP connections until it is complete. Then, I'll initialize each route
file by calling a named method and pass the database connection to it.
Or, you could just always call connect in each module, yet cache the value. (The connect call would need to be called within the route callback code so that the routes were defined immediately and not when the connection was actually established).
// db.jsvar _db = null;
exports = function(callback) {
if (!_db) {
_db = {}; // only one connection, so we'll stop others from tryingrequire('mongodb').MongoClient.connect(/* the URL */, function (err, db) {
_db = db;
callback(err, db);
});
} else {
callback(null, _db);
}
};
// app.jsvar db = require('./db');
db(function(err, connection) {
// store the connection value here and pass around, or ...// call this always in each file ...
});
/// or ...
app.get('/', function (req, res) {
db(function(err, connection) {
connection.collection(/* … */);
});
});
Or, you could use MongooseJS (a wrapper for the native NodeJS MongoDB driver) where commands, etc. are queued if the connection isn't available yet ....
Post a Comment for "How To Export An Object That Only Becomes Available In An Async Callback?"