Skip to content

Instantly share code, notes, and snippets.

@jskod
Created September 13, 2019 07:44
Show Gist options
  • Save jskod/b0c8fc162b71b0eb8f931cf9bef7ba48 to your computer and use it in GitHub Desktop.
Save jskod/b0c8fc162b71b0eb8f931cf9bef7ba48 to your computer and use it in GitHub Desktop.
Multi-Tenant Mongoose Model Wrapper For NodeJS Based Applications
import mongoose, { Schema } from 'mongoose'
import { getCurrentTenantId } from './storage'
/**
Function will return another function, which will further return a mongoose discriminator.
The only difference here is that everytime you use the mongoose model, you will have to call a function,
but it will give you a freedom of passing tenant-based data.
*/
export function tenantModel(name, schema, options) {
return (props = {}) => {
// add new props to the schema
schema.add({ tenantId: String })
// create a mongoose model
const Model = mongoose.model(name, schema, options)
const { skipTenant } = props
// getCurrentTenantId() is a function will returns current tenant
// this function uses continuation-local-storage npm package
const tenantId = getCurrentTenantId()
// if no tenant or you want to skip tenant specific descrimintor, return actual model
if (!tenantId || skipTenant) return Model
// set the descriminatorKey for the model
Model.schema.set('discriminatorKey', 'tenantId')
// set the descriminator name
const discriminatorName = name + '-' + tenantId
// check if a descriminator already exists
const existingDiscriminator = (Model.discriminators || {})[discriminatorName]
// if it does exist, simply return that. Otherwise create new
return existingDiscriminator || Model.discriminator(discriminatorName, new Schema({}), `${tenantId}`)
}
}
// this function will return simple model,
// so that you write the consistent code like calling the Model function to access the model
export function tenantlessModel(name, schema, options) {
return () => mongoose.model(name, schema, options)
}
import { createNamespace } from 'continuation-local-storage';
// define namespace name
const namespaceName = 'request';
// create namespace instance
const ns = createNamespace(namespaceName);
// export function to bind current namespace to the req and res
export function bindCurrentNamespace(req, res, next) {
ns.bindEmitter(req);
ns.bindEmitter(res);
ns.run(() => {
next();
});
}
// export function to set tenantId for current request being processed
export function setCurrentTenantId(tenantId) {
return ns.set('tenantId', tenantId);
}
// export function to get previously set tenantId of request
export function getCurrentTenantId() {
return ns.get('tenantId');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment