npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mongoose-multitenant

v0.8.2

Published

Wrapper for Mongoose that allows for easy horizontal multitenancy (collection prefix per tenant)

Downloads

37

Readme

Mongoose Multitenant

Build Status

This package facilitates horizontal multitenancy in one MongoDB database, obviously using Mongoose as the interaction layer.

Basics

With this package, you can use one schema per model, as you would normally with Mongoose, and then use special methods and syntax apply that schema to different tenant collections.

Instead of using mongoose.model(name, schema) to compile your model, you would now use mongoose.mtModel(name, schema). This still creates the Mongoose model as normal, but adds some additional functionality. Specifically, you can retrieve a model for a specific tenant using this syntax:

mongoose.mtModel('tenantId.modelName')

When that happens, the package will check if that model for that tenant has already been compiled. If not, it creates a copy of the base model's schema, updates any refs to other collections, and then compiles a new model with the new schema, with a collection name of tenantId__originalCollectionName. All per-tenant models are lazy-loaded, meaning that they won't take up memory until they are needed.

Usage

Pull in requirements

var mongoose = require('mongoose');
require('mongoose-multitenant');

mongoose.connect('mongodb://localhost/multitenant');

Changing collection delimiter

Thanks to @watnotte for this - if you want to change the delimiter from the default __ you can do the following:

require('mongoose-multitenant')('CUSTOM_DELIMITER');

Create a schema

With mongoose-multitenant you use all the same syntax for schema creation as you normally do with Mongoose, with the addition of the $tenant property on document references. This tells the system whether it is a reference to a document for the same tenant (true) or a root-level document without a tenant (false)

var barSchema = new mongoose.Schema({
    title:String,
    _foos:[{
        type:mongoose.Schema.Types.ObjectId,
        ref:'Foo',
        $tenant:true
    }]
});

var fooSchema = new mongoose.Schema({
    title:String,
    date:Date
});

Compile the models

Instead of using mongoose.model to compile, you use mongoose.mtModel.

mongoose.mtModel('Bar', barSchema);
mongoose.mtModel('Foo', fooSchema);

Use the models

Basic usage:

// This returns a new Foo model for tenant "tenant1"
var fooConstructor = mongoose.mtModel('tenant1.Foo');
var myFoo = new fooConstructor({
    title:'My Foo',
    date:new Date()
});

myFoo.save(function(err, result) {
    // This saved it to the collection named "tenant1__foos"
});

And make use of refs/populate:

var barConstructor = mongoose.mtModel('tenant1.Bar');
var myBar = new barConstructor({
    title:'My Bar'
    _foos:[myFoo._id]
});

myBar.save(function(err, result) {
    // Saved to the collection named "tenant1__bars"

    barConstructor.find().populate('foos').exec(function(err, results) {
        console.log(results[0]._foos[0].title); // "My Foo"
    });
});

But you can't populate across tenancies:

var tenant2Bar = mongoose.mtModel('tenant2.Bar');
var newBar = new tenant2Bar({
    title:'New Bar',
    _foos:[myFoo._id]
});

newBar.save(function(err, result) {
    tenant2Bar.find().populate('foos').exec(function(err, results) {
        console.log(results[0]._foos[0]); // "undefined"
    });
});

Helper Methods

In addition to this base functionality, each per-tenant model gets two new schema methods: getTenantId() and getModel().

getTenantId()

This does what you think it does - returns the tenant ID for the model.

getModel()

This can be used in your mongoose middleware methods to get the related model class. E.g.

barSchema.pre('save', function(next) {
    
    // This gets Foos in the same tenancy as this Bar
    this.getModel('Foo').find({_id:{$in:this._foos}}, function(err, foos) {
        // Do something to the related Foos.
        next()
    });
});

Credits

  • Brian Kirchoff for his great mongoose-schema-extend package