Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing a Mongoose instance between multiple NPM packages

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:

  • We really do want to include the apps in a host application, running in a single process, rather that having multiple Node instances. The host contains middleware for authentication and other things.
  • We do want to have the apps as separately developed NPM packages included as versioned dependencies of the various host applications that we build, rather than just copying their source to a host application.
  • We realize that reusing the same Mongoose instance between multiple mounted apps will have them share the same model namespace.

Edit: To clarify the package structure after all has been npm installed:

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?

like image 612
Greg Avatar asked Oct 05 '12 22:10

Greg


2 Answers

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.

like image 60
Bill Avatar answered Oct 16 '22 21:10

Bill


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.

like image 42
jbmusso Avatar answered Oct 16 '22 20:10

jbmusso