Sequelize: connect to database on run time based on the request - node.js

I am working on a node.js app where I need to connect to more than one databases. One of the database is central database which contains information common to all. And then there are country level databases where data is stored according to the countries.
I am using sequelize ORM in the app.
Database is postgresql.
Framework is express.
The problem is I want to decide on runtime based on the request which database to use and models should automatically connect to the appropriate database. I have seen this question but didn't found it helpful.
I have also checked in another forums but didn't find anything.

You need to create objects corresponding to each of your database, and at each this object you need to instantiate the Sequelize. Further, for each sequelize instance you need to import the models (assumming that all those databases have exactly the same tables and model representations).
import Sequelize from 'sequelize';
let connectionsArray = [
'postgres://user:pass#example.com:5432/country1',
'postgres://user:pass#example.com:5432/country2',
'postgres://user:pass#example.com:5432/country3',
];
let country1DB, country2DB, country3DB;
country1DB = country2DB = country3DB = {};
country1DB.Sequelize = country2DB.Sequelize = country3DB.Sequelize = Sequelize;
country1DB.sequelize = new Sequelize(connectionsArray[0]);
country2DB.sequelize = new Sequelize(connectionsArray[1]);
country3DB.sequelize = new Sequelize(connectionsArray[2]);
// here you need to access the models path, maybe with fs module
// iterate over every model and import it into every country sequelize instance
// let's assume that models' paths are in simple array
models.forEach(modelFile => {
let model1DB = country1DB.sequelize.import(modelFile);
let model2DB = country2DB.sequelize.import(modelFile);
let model3DB = country3DB.sequelize.import(modelFile);
country1DB[model1DB.name] = model1DB;
country2DB[model2DB.name] = model2DB;
country3DB[model3DB.name] = model3DB;
});
// now every country?DB object has it's own sequelize instance and all model definitions inside
export {
country1DB,
country2DB,
country3DB
};
This is just some example code, it would need refactor to be useful (introduce some loops etc.). It should just show you the idea of how to use multiple databases within single application. If you would like to use e.g. country1 database somewhere, you would simply do
import { country1DB } from './databases';
country1DB.User.findAll({...});
Above code would perform SELECT * FROM users in previously specified country1 database. Example express route could look like below:
import * as databases from './databases';
app.get('/:dbIndex/users', (req, res) => {
databases['country' + req.params.dbIndex + 'DB'].User.find().then(user => {
res.json(user.toJSON());
});
});
Or, even better, you could write some middleware function that would be run before every request and which would be responsible for choosing proper database for further operations.

Related

loopback3: associate users to different databases

I'm developing a project in loopback3 where I need to create accounts for multiple companies, where each compnay has its own database, I'm fully aware that the loopback3 docs has a section where they explain how to create datasources programmatically and how to create models from that datasource, and I've used that to create the following code which receives in the request a parameter which i called dbname and this one changes the linking to the wanted datasource..
userclinic.js
Userclinic.observe('before save', async (ctx, next) => {
const dbname = ctx.instance.dbname; // database selection
const dbfound = await Userclinic.app.models.Clinics.findOne({where:{dbname}}) // checking if that database really exist in out registred clients databases
if( dbfound ){ // if database found
await connectToDatasource(dbname, Userclinic) // link the model to that database
} else { // otherwise
next(new Error('cancelled...')) // cancel the save
}
})
utils.js (from where i export my connectToDatasource method)
const connectToDatasource = (dbname, Model) => {
console.log("welcome");
var DataSource = require('loopback-datasource-juggler').DataSource;
var dataSource = new DataSource({
connector: require('loopback-connector-mongodb'),
host: 'localhost',
port: 27017,
database: dbname
});
Model.attachTo(dataSource);
}
module.exports = {
connectToDatasource
}
So my problem is that the datasource is actually really changing but the save happens in the previous datasource that was selected (which means it saves the instance to the old database) and doesn't save to the new one till I send the request again. so chaging the datasource is taking two requests to happen and it's also saving the instance in both databases.
I guess that when the request happen loopback checks the datasource related to that model first before allowing any action on that model, I really need to get this done by tonight and I wish someone can help out.
PS: if anyone has a solution to this or knows how to associate multiple clients (users) to multiple databases (programmatically of course) in any way using loopback 3 I'm all ears (eyes).
Thanks in advance.

Imported Sequelize connection works properly in one file but not another

I am writing a NodeJS API with express that uses Sequelize as an ORM to connect to a postgres database.
I have several API files containing the endpoint functions for each model, and I group related functions in these files.
In these files, I load the db connection by requiring the models folder, which contains all the model definitions, and an index file that instantiates & exports the database connection with the models.
I instantiate this at the top of any file that needs access to the database. My problem is that when I enter an endpoint, I can access the database connection perfectly. But when I call any function from another file that also accesses the database, all of my models from the required file are undefined, and it throws an error.
/* api/fooApi.js */
const db = require('../models')
const logApi = require('../logApi')
async function createFoo(req, res, next) {
try {
// db.foo and db.log are defined here, and accessible
const foo = await db.foo.create(req.body)
const log = await logApi.logCreation('foo', foo)
}
}
module.exports = { createFoo }
/* api/logApi.js */
const db = require('../models')
async function logCreation(recordType, record) {
// db.foo and db.log are not defined here when the function is called from fooApi.js
const log = await db.log.create({
event: 'create',
type: recordType,
details: `${recordType} record created by user ${record.createdBy}`
})
return log
}
module.exports = { logCreation }
When I enter the endpoint function createFoo(), I have full access to everything that I expect to be in db, including db.foo.create(). But in logCreation(), I cannot access these same functions.
Many different models will access the logCreation function, so it needs to be defined in one place. The require statement at the top of the file is exactly the same as that in fooApi, but when I debug the function, db is an empty object without any of the properties it should have.
If I pass db as an argument to logCreation, then the function works, but I'd like to avoid this if I can, as it would involve a major restructuring.
Previously to having thing set up this way, I had the following in each api file:
let db = null
function init (dbConn) {
db = dbConn
}
At setup, I would call init() in every API file using the same instance as the argument. However, this was a super clunky way of doing things that I wanted to move away from.
So my question is: What is the correct way to set up Sequelize so that I can access the database across multiple files?
I am using implicit transactions using namespace and cls-hooked as directed in the docs.
I solved my problem - this had nothing to do with how I was importing the file, and was entirely due to a circular dependency because a helper function on one of the models was accessing a helper function in an API file.

Create Bookshelf.js models without connection and make connection in route files

I have Bookshelf.js models (a general model with parse() and format() functions and a model for each table that extends the general model) and routes that use these models. The database is PostgreSQL. But I have users in a SQLite database and each user has its own PostgreSQL connection options. I can retrieve the current user and expose its database connection options in the request, so every route knows how to connect to PostgreSQL. But how can I connect and use my Bookshelf.js models in a route? This is an example of what I am trying to do in a routes file:
var express = require('express'),
knex = require('knex'),
// This should be the file that exposes all models.
// Every model exposed, extends the general model.
// But it already requires a connection when init Bookshelf.js...
dataBookshelf = require('../models'),
router = express.Router();
router.get('/items/:id',function(req,res,next) {
// In req.db I have PostgreSQL connection options for the current user.
var connection = req.db;
// Make a connection to PostgreSQL.
var db = dataBookshelf(knex(connection));
// Use Items model.
var Items = db.Items;
Items.read(req.params.id).then(function(item) {
res.json(item);
}).catch(Items.NotFoundError,function() {
var err = new Error('Item not found.');
res.status(400).json(err);
});
});
module.exports = router;
If there is another way to save the current user connection options globally and to use them in the module that exposes Bookshelf.js instance, instead of passing them in the request, it should be better and safer.

Using Express.js 3 with database modules, where to init database client?

Knowing that Express.js pretty much leaves it to developer on deciding app structure, and after reading quite a few suggestions on SO (see link1 and link2 for example) as well as checking the example in official repo, I am still not sure if what I am doing is the best way forward.
Say I am using Redis extensively in my app, and that I have multiple "models" that require redis client to run query, would it be better to init redis client in the main app.js, like this:
var db = redis.createClient();
var models = require('./models')(db);
var routes = require('./controllers')(models);
or would it be better to just init redis in each model, then let each controller require models of interests?
The latter approach is what I am using, which looks less DRY. But is passing models instance around the best way? Note that I am loading multiple models/controllers here - I am not sure how to modify my setup to pass the redis client correctly to each models.
//currently in models/index.js
exports.home = require('./home.js');
exports.users = require('./user.js');
TL;DR, my questions are:
where best to init redis client in a MVC pattern app?
how to pass this redis client instance to multiple models with require('./models')(db)
Update:
I tried a different approach for index.js, use module.exports to return an object of models/controllers instead:
module.exports = function(models){
var routes = {};
routes.online = require('./home.js')(models);
routes.users = require('./user.js')(models);
return routes;
};
Seems like a better idea now?
Perhaps it's useful if I share how I recently implemented a project using Patio, a SQL ORM. A bit more background: the MVC-framework I was using was Locomotive, but that's absolutely not a requirement (Locomotive doesn't have an ORM and it leaves implementing how you handle models and databases to the developer, similar to Express).
Locomotive has a construct called 'initializers', which are just JS files which are loaded during app startup; what they do is up to the developer. In my project, one initializer configured the database.
The initializer established the actual database connection, also took care of loading all JS files in the model directory. In pseudocode:
registry = require('model_registry'); // see below
db = createDatabaseConnection();
files = fs.readDirSync(MODEL_DIRECTORY);
for each file in files:
if filename doesn't end with '.js':
continue
mod = require(path.join(MODEL_DIRECTORY, filename));
var model = mod(db);
registry.registerModel(model);
Models look like this:
// models/mymodel.js
module.exports = function(db ) {
var model = function(...) { /* model class */ };
model.modelName = 'MyModel'; // used by registry, see below
return model;
};
The model registry is a very simple module to hold all models:
module.exports = {
registerModel : function(model) {
if (! model.hasOwnProperty('modelName'))
throw Error('[model registry] models require a modelName property');
this[model.modelName] = model;
}
};
Because the model registry stores the model classes in this (which is module.exports), they can then be imported from other files where you need to access the model:
// mycontroller.js
var MyModel = require('model_registry').MyModel;
var instance = new MyModel(...);
Disclaimer: this worked for me, YMMV. Also, the code samples above don't take into account any asynchronous requirements or error handling, so the actual implementation in my case was a bit more elaborate.

efficiency of mongodb/mongoskin access by multiple modules approach?

I'm developing an express app that provides a REST api, it uses mongodb through mongoskin. I wanted a layer that splits routing from db acess. I have seen an example that creates a database bridge by creating a module file, an example models/profiles.js:
var mongo = require('mongoskin'),
db = mongo.db('localhost:27017/profiler'),
profs = db.collection('profiles');
exports.examplefunction = function (info, cb) {
//code that acess the profs collection and do the query
}
later this module is required in the routing files.
My question is: If I use this aproach for creating one module for each collection, will it be efficient? Do I have an issue of connecting and disconnecting multiple(unnecessary) times from mongo by doing that?
I was thiking that maybe exporting the db variable from one module to the others that handle each collection would solve the suposed issue, but I'm not sure.
Use a single connection and then create your modules passing in the shared db instance. You want to avoid setting up separate db pools for each module. One of doing this is to construct the module as a class.
exports.build = function(db) {
return new MyClass(db);
}
var MyClass = function(db) {
this.db = db;
}
MyClass.doQuery = function() {
}

Resources