How we implemented multi-tenancy in our Node.js App

Single tenant application
Multi-Tenancy with DB Connector Service
Multi-tenancy with logical separation of data
  • Easy client on-boarding: New client on-boarding is easier as we do not need to provision a new database for every new client.
  • No need to change in the “backend” code for every new client. Option 1 would require change in the DB Connector service.
  • Creating client specific react app allowed us to do UI branding for the client.
const async = require(‘async’);
const columnName = ‘tenant_id’;
const columnSpec = {
type: ‘int’,
};
exports.up = function (db, callback) {
async.series([
db.addColumn.bind(db, ‘table1’, columnName, columnSpec),
db.addColumn.bind(db, ‘table2’, columnName, columnSpec),
db.addColumn.bind(db, ‘table3’, columnName, columnSpec)
], callback);exports.down = function (db, callback) {
async.series([
db.removeColumn.bind(db, ‘table1’, columnName),
db.removeColumn.bind(db, ‘table2’, columnName),
db.removeColumn.bind(db, ‘table3’, columnName)
], callback);
const a2 = sequelize.define(‘users’, {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, unsigned: true},
username: Sequelize.STRING(50),
email: Sequelize.STRING(50),
});
const a2 = sequelize.define(‘users’, {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, unsigned: true},
username: Sequelize.STRING(50),
email: Sequelize.STRING(50),
tenant_id: Sequelize.INTEGER,
}, {
defaultScope: {
where: {
tenant_id: tenantId,
// tenantId is passed to the function which initializes the object and this is the only way to create an object within the app
},
},
});
.find, .findAll, .count, .update and .destroy.
users.hook(‘beforeValidate’, function (model) {
model.tenant_id = tenantId;
});
const initializeUsers = function (tenantId) {
const users = sequelize.define(‘users’, {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, unsigned: true},
username: Sequelize.STRING(50),
email: Sequelize.STRING(50),
tenant_id: Sequelize.INTEGER,
}, {
defaultScope: {
where: {
tenant_id: tenantId,
},
},
});
users.hook(‘beforeValidate’, function (model) {
model.tenant_id = tenantId;
});
return users;
};
module.exports = initializeUsers;

--

--

--

Senior Staff Engineer @freshworks. Ex-McKinsey/Microsoft/Slideshare/SAP, Tech Enthusiast, Passionate about India. Opinions are mine

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Abhinav Dhasmana

Abhinav Dhasmana

Senior Staff Engineer @freshworks. Ex-McKinsey/Microsoft/Slideshare/SAP, Tech Enthusiast, Passionate about India. Opinions are mine

More from Medium

How to secure Client-Side React apps with protected routes using Apps’ memory!?

The Comparison Between Node.JS and Golang: Which One to Choose?

Laravel Nova — Single Responsibility Principle Approach

How To Store And Retrieve RedisJSON on Redis Cloud using Node.js