Multi tenant (SAAS) using nodejs sequelize - node.js

I am trying to build a multi tenant ( / Software as a service) using nodejs and postgres, sequelize as ORM. I decided to go with separate DBs for each client, rather than having single DB with all table having the extra column, because of security reasons. I achieved the result, but performance was not good, since i have to initialise models for each DB according to sequelize(for almost each request). Is there any better way to do this? Am I missing something in sequelize?

A quick implementation of my comment above.
app.js:
const Sequelize = require('sequelize');
const connections = {
client1: new Sequelize('postgres://user:pass#example.com:5432/client1'),
client2: new Sequelize('postgres://user:pass#example.com:5432/client2'),
client3: new Sequelize('postgres://user:pass#example.com:5432/client3'),
};
const User = require('./models/user');
const Post = require('./models/post');
const Comment = require('./models/comment');
Object.keys(connections).forEach(connection => {
connection.define('User', userColumns);
connection.define('Post', postColumns);
connection.define('Comment', commentColumns);
});
models/user.js:
module.exports = {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: Sequelize.STRING,
email: Sequelize.STRING
// etc, etc
};
Obviously whatever server framework you use you'll need to detect (from the url I imagine) which client connection to use for a given request.
Alternatively, consider writing a single-connection app and deploying multiple instances of it (I'm doing this currently). Might be a simpler choice if you're set on separate DBs.

I prefer the schema approach to multi tenancy in Postgres. I'm no authority on security but it should be better than table based multi tenancy. Disaster recovery should also be slightly easier than table based MT, but still worse than with separate DBs.
I managed to implement this in Sequilize using ES6 Proxies. This puts a hard requirment on your Node version, but if you're willing to use at least v6.9.2 you could give my lib a try:
https://www.npmjs.com/package/sequelize-multi-tenant-enhancer

Related

How can two different Node.js servers communicate to one mongoose schema?

I have two servers (Public server and Admin Server) and I would like to access from both one mongoose schema. The main server is the Public one where everything happens, but sometimes, from Admin server I would like to access that schema. I could write the schema on both the servers, but that would mean bad code. If that is the only solution, I will do it. But, is there any other way of doing this? For MongoDB I have a third server, that is only for database. Could I write something there so that when I connect with mongoose to the MongoDB server to receive the model from there?
Let's say I have this code (somewhere, I don't know where yet).
const mongoose = require('mongoose');
const postSchema = mongoose.Schema({
title: {
type: String,
required: true,
}
});
const Post = new mongoose.model('Post', postSchema);
module.exports = Post;
What I am trying to do in a server file is for example to call Post.save() or whatever function I am trying to get, without having the schema on both servers.
I used Mongoose-Gen npm and created an API in order to get the schema from on server to another.

how to use passport.js local strategy without creating a schema,or database model

I was trying to understand how to use the passport.js in Mongodb native driver,Express now the problem is that all the reference or tutorials are showing the LOCAL-STRATEGY by using mongoose which creates a schema or model........so now iam STUCK
Take a look at the mongodb documentation for their Nodejs driver.
mongoDB Node Driver
Sorry for being here for a little bit late, but maybe my answer would be helpful to others who seeking answer for this kind of question.
I assume that you were struggling with these problems:
How to reuse database connection among your project
You can define your MongoClient object once and reuse this across multiple modules in your project as follow:
dbUtil.js hold definition of MongoClient object:
const MongoClient = require('mongodb').MongoClient;
const SERVER_URI = // link to your server, for example: 'http://localhost:27017';
const DB_NAME = // database name, for example: 'test';
/* #WHY?:
(option 1?) : to let the server assign objectId instead of Node driver,
(option 2 & 3?) : to get rid of deprecation warnings
*/
const clientObj = new MongoClient(`${SERVER_URI}/${DB_NAME}`, {
forceServerObjectId: true,
useNewUrlParser: true,
useUnifiedTopology: true
});
module.exports = {
client: clientObj,
dbName: DB_NAME
}
In another module where you need to use the defined connection:
const { client, dbName } = require('dbUtil');
// Because client.connect() return a Promise, you should wrap everything
// inside an immediately-invoked expression like this
(async () => {
await client.connect(); // at first you need to open the connection client
const dbO = await client.db(dbName); // get the connection to database
/* perform database operations, for example:
dbO.collection(users).insertOne({ name:'mongoadmin' });
*/
client.close(); // remember to close the connection when you're done
})();
So instead of the Mongoose way of using User.find().exec(), in Mongo native driver you have to activate connection to Client first and then use client.dbO.collection('users') (which return a Promise).
What the heck is Passport and why it's needed for your project
Passport is authentication middleware for Express that support authentication from Facebook, Google, JWT,... and many other authentication strategies. It can be helpful when you need to you want to support authentication from multiple authentication portal. However, it's not a must-have.
Sometimes applying another layer of abstraction from third-party libraries not only bring no sense to you & your project, but also over-complicate your existed code base. You'd chose not to use Mongoose and adapted MongoDb native driver instead, stated that you didn't need schema & model stuffs. For the same logic, I don't see any necessity of adapting Passport. This link can be helpful to you in some way: another Stackoverflow post
To apply authentication using JSON web token to your Express routes, you need to do these following steps:
Generate token for user signed in
Verify token
Define protected routes and write middlewares for those
All these tasks can be done without any third-party modules/libraries!
I believe your question stems from using mongodb schema validation instead of mongoose schema. You can use another means of authentication like JWT which does not directly need models for its authentication.

How to share mongoose models with multiple microservices

I've a User model which looks like:
import mongoose from 'mongoose';
const UserSchema = new mongoose.Schema({
name: String,
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
export default mongoose.model('User', UserSchema);
I'm trying to share this model with multiple microservices, but how do i share this? Should i make a database service exposed over http or should i manually make models in each server and use it that way, or is there any other way to do the same?
It's dangerous to share schemas across microservices because they could become very coupled, or at least not like that. It's normal that microservices use data from each other, but models should not be fully imported in another microservice. Instead, the dependent microservices should use a subset, a local representation of the remote model. For this you should use an Anti-corruption layer. This ACL would receive as input remote models and produce as output a local, immutable/readonly representation of that model. The ACL lives at the outer boundary of the microservice, i.e. where the remote calls are made.
Also, sharing *schema.js files across microservices would force you to use JavaScript/NodeJS in all the other microservices, which is not good. Each microservice should use whatever programming language is best suited for it.
Should i make a database service exposed over http
The database is private to the owning microservice. It should not be exposed.

Split mongoose models into subsets based on session variable

I'm migrating an application from MySQL to Node/Mongoose running on Express. Currently in my LAMP stack I have an "account_id" column in several tables, and according to the "active account" in the session, the system automatically queries according to the account_id. Obviously I can do the same in my new setup, but seeing as I'm new to Node, Mongoose, and NoSQL in general, I was wondering if there is a better way to accomplish this with this new technology.
I was thinking:
1) Use multiple databases (but I can't figure out how to determine database from session unless I connect to the database separately on each request)
2) Add a prefix to the collection name, but then I'd have to reconstruct the mongoose.model object on every request, which I guess wouldn't be so bad.
What are the costs and benefits to these and other solutions?
What I would do is to have a user model and have it be connected to the other models of your project like so:
mongoose.model('User', new Schema({
name: String,
email: String,
}))
mongoose.model('Other', new Schema({
user: {type: ObjectId, ref: 'User'},
name: String,
}))
It shouldn't be much different from you MySQL database.

Abstract layer for Node.js database

I have been looking around for simple database abstraction implementation, then i found great article http://howtonode.org/express-mongodb, which old but I still like the idea.
Well maybe the construction, could take some kind of object literal with database settings.
So the main idea is that there could be different implementations of UserService-s, but locate in different directories and require only the one that's needed.
/data-layer/mongodb/user-service.js
/post-service.js
/comment-service.js
/data-layer/couchdb/user-service.js
/post-service.js
/comment-service.js
When the Database is needed, I wil get it with var UserService = require(__dirname + '/data-layer/mongodb/user-service).UserService(db); where var db = "open db object"
Would this be the correct way to do it or is there any better solutions ?
There are a few solutions, available via NPM :
Node-DBI : "Node-DBI is a SQL database abstraction layer library, strongly inspired by the PHP Zend Framework Zend_Db API. It provides unified functions to work with multiple database engines, through Adapters classes. At this time, supported engines are mysql, mysql-libmysqlclient and sqlite3". Looks like the developpment has been paused.
Accessor : "A database wrapper, provide easy access to databases." Supports only MySQL and MongoDB at the moment.
Activerecord : "An ORM written in Coffeescript that supports multiple database systems (SQL, NoSQL, and even REST), as well as ID generation middleware. It is fully extendable to add new database systems and plugins."
Update:
Since I've posted this answer, I have abandoned mongoose for official MongoDB NodeJS Drivers as it is fairely intuitive and more loyal to the concept of NoSQL databases.
Original Answer:
I though it might be time to update the answer of an old question:
If you want to use MongoDB as your document-oriented database, mongoose is a good choice and easy to use (example from official site):
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var Cat = mongoose.model('Cat', { name: String });
var kitty = new Cat({ name: 'Zildjian' });
kitty.save(function (err) {
if (err) // ...
console.log('meow');
});
For a rather modern approach, Mongorito is a good ODM which uses ES6 generators instead of callbacks.
As of 06.2015 I reckon that the best ORM for SQL databases with Node.js/io.js is Sequelize supporting the following databases:
PostgreSQL
MySQL
MariaDB
SQLite
MSSQL
The setup is fairly easy:
var sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
});
// Or you can simply use a connection uri
var sequelize = new Sequelize('postgres://user:pass#example.com:5432/dbname');
It also provides transactions, migrations and many other goodies.

Resources