My CMS is written in Node.js + Express + Mongoose. It's a multisite CMS and each site has its own database. So I needed to make Mongoose to switch the connection at every HTTP request. I looked around for some solution, or someone who had my same condition, but without success. So this is my solution:
HTTP Request:
router.get('/site/pages', function (req, res) {
pagesBiz.list(req.session, function(err, list) {
//do stuff
});
});
BIZ component (pagesBiz):
module.exports = {
list: function(session, callback) {
Page(session.database).find(callback);
}
}
Model (Page)
var cached = {};
var getModel = function (database) {
if(! cached[database]) {
var conn = mongoose.createConnection('mongodb://localhost/' + database);
cached[database] = conn.model('Page', pageSchema);
}
return cached[database];
}
module.exports = function(database) {
return getModel(database);
}
So how does it works? When the user logs in a new user session is created and bound to the user via the session cookie (I use MongoStore + Express Session). The session contains the name of the database that is used to dynamically instantiate the Mongoose models.
It works perfectly, users of different sites read from their own databases without the risk of "collisions", on the other side I have to pass the session (or the database name) around the functions, but I guess this is how Node.js works in a multithreaded context.
The only problem is when I use the populate method(), I get this error:
f:\www\rudsjs\node_modules\mongoose\lib\connection.js:625 throw new MongooseError.MissingSchemaError(name); ^ MissingSchemaError: Schema hasn't been registered for model "User". Use mongoose.model(name, schema) at NativeConnection.Connection.model (f:\www\rudsjs\node_modules\mongoose\lib\connection.js:625:11) at populate (f:\www\rudsjs\node_modules\mongoose\lib\model.js:2136:24) at Function.Model.populate (f:\www\rudsjs\node_modules\mongoose\lib\model.js:2101:5) at Object.cb (f:\www\rudsjs\node_modules\mongoose\lib\query.js:1159:16) at Object._onImmediate (f:\www\rudsjs\node_modules\mongoose\node_modules\mquery\lib\utils.js:137:16) at processImmediate [as _immediateCallback] (timers.js:336:15)
Process finished with exit code 8
I tried to preload the model before the populate() call using this:
User(session.database);
but the problem seems to be related to the way Mongoose caches the models (that is not mine), I looked at connection.js
model = this.base.models[name];
if (!model) {
throw new MongooseError.MissingSchemaError(name);
}
so I'd need a way to insert my model inside this.base.models. Do you have any idea? Do you especially have a better solution to implement a multi-site/multi-database environment?