In an attempt to modularize a large existing Node+Express+Mongoose application into multiple mountable apps, each developed as a separate NPM package, we're wondering whether sharing a single Mongoose instance between them is a good idea?
Let's say we have a suite of NPM packages each containing client-side assets, Mongoose models, and a REST-API implemented with Express. They do share a few common traits but are essentially to be considered separate reusable artefacts. A host application, also Express-based, mounts these under various root URIs:
var discussions = require('discussions'),
tickets = require('tickets'),
events = require('events'),
express = require('express'),
app = express();
var environment = { ...see below... };
...
app.use('/events-api', events(environment));
app.use('/tickets-api', tickets(environment));
app.use('/discussions-api', discussions(environment));
Now, since the events
, tickets
and discussions
apps (separate NPM packages
pulled in via the host package.json
) use Mongoose, as do the
host application itself, we figured we would pass in the host Mongoose instance
through some kind of environment
object that also include other
stuff that the host wants to share with the mounted apps.
Do you see any obvious flaws with this approach? The mounted apps
in this case would not specify Mongoose as a dependency in their
respective package.json
, and they would not require('mongoose')
as normally done but instead get the Mongoose instance from the host
which is responsible for connecting it to MongoDB.
If this is a bad idea and you suggest each sub-app declare a dependency towards Mongoose on their own, each NPM package would get their own copy of Mongoose and would each have to connect to MongoDB, right?
Some background info:
Edit: To clarify the package structure after all has been npm install
ed:
host/
assets/
models/
routes/
node_modules/
express/ ...
mongoose/ ...
events/
assets/ ...
models/ ...
routes/ ...
tickets/
assets/ ...
models/ ...
routes/ ...
discussions/
assets/ ...
models/ ...
routes/ ...
That is, the events
, tickets
, and discussions
apps do not include
Mongoose (or Express) of their own but are designed to rely on an always-present
host application that suppliesd those dependencies.
We're assuming here that an NPM package like tickets
cannot simply
require
stuff from the parent, right?
If you want to reuse your Mongoose package between other NPM packages, the best way to do it is to install the shared package at the top level app and then use it to initialize the other NPM packages.
In the top level:
var db = require('myMongooseDb'),
events = require('events')(db),
...
Then your events package just needs to export a function that takes the db as a parameter.
I suggest you have a look at https://github.com/jaredhanson/node-parent-require, a recently published package which solved this issue for me.
The node-parent-require Readme file on the Github project page provides a detailed walkthrough using mongoose.
Basically, you need to dig in your submodule and replace this:
mongoose = require("mongoose");
... with this:
try {
var mongoose = require('mongoose');
} catch (_) {
// workaround when `npm link`'ed for development
var prequire = require('parent-require')
, mongoose = prequire('mongoose');
}
Don't forget to add mongoose as a peerDependency in your submodule's package.json. For example:
"peerDependencies": {
"mongoose": "3.x"
}
You may also want to read http://blog.nodejs.org/2013/02/07/peer-dependencies/ first.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With