How to reuse the same sequelize connection anywhere in my Express app? - node.js

Here is my problem, I have an Express app where I separate the controllers (i.e. the functions called for each route) from the application entry point:
index.js
...
const sequelize = new Sequelize(...);
const PersonController = require('controllers/PersonController');
router.route('/persons').get(PersonController.index)
...
controllers/PersonController.js
...
exports.index = (req, res) => {
//Return all the lines of the `Persons` table
};
...
But to access the database, I must have access to the sequelize instance in my controllers. How should I do?
Thank you for your help.

There are a lot of ways...
My favorite : you can create your constroller as a class...
Here is a kind of code, (not the best, but something running)
Controller class
class PersonController {
constructor(sequelize) {
this.sequelize = sequelize;
}
index(req, res){
const sequelize = sequelize;
... your code ...
}
}
module.exports = PersonController;
After
const sequelize = new Sequelize(...);
const PersonController = require('controllers/PersonController');
router.route('/persons').get((req, res) => {
const controller = new PersonController(sequelize);
controller.index(req, res);
})

Related

How to pass sequelize through express routes to controller in MVC model in node when using ES6 import/export?

I'm trying to refactor some existing code into an MVC model, and am not sure if I'm messing up the structure, or if I just can't figure out how to pass a variable, but, assuming my structure is good, how do I pass a sequelize instance through an Express route to a controller? Here's my code, hopefully simplified for clarity:
Structure:
src/
db.js
routes.js
server.js
controllers/mycontroller.js
models/mymodel.js
server.js:
'use strict';
import express from 'express';
import Sequelize from 'sequelize';
import { router as routes } from './routes';
import db from './db';
const app = express();
try {
await db.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
db.myTable.sync(); // this works fine
app.use(express.urlencoded({ extended: false }));
app.use(routes); // this, I think, needs to pass db.myTable
app.listen( 3300, () => {
console.log('Listening on 3300');
});
db.js:
'use strict';
import Sequelize from 'sequelize';
import myTableModel from './models/mymodel';
const sequelize = new Sequelize('sqlite::memory');
const db = {};
db.authenticate = () => sequelize.authenticate();
db.myTable = myTableModel(sequelize);
export default db;
routes.js:
import express from 'express';
export const router = express.Router();
import MyController from './controllers/mycontroller';
const myController = new MyController();
... // other routes elided for brevity
router.post('/test', myController.store); // that db.myTable I thought I needed to pass above,
// I think I need to pass again here. Or, alternatively, I could put a constructor into
// MyController and pass it as an arg above when I call 'new MyController', but I still have to
// get it down here into this routes file.
mycontroller.js:
'use strict';
import MyTableModel from '../models/mymodel'; // This was an experiment I tried, but in retrospect,
// it of course makes no sense. I don't need to import the model, I need to have passed the
// instantiated model down here somehow
export default class MyController {
store = async (req, res, next) => {
await MyTable.create({ full: req.body.fullUrl}); // This fails (of course), because
// MyTable.create doesn't exist here.
res.redirect('/');
}
}
So, back to the question: assuming this structure looks correct (feel free to comment on that as well), how do I get that MyTable sequelize object passed all the way through to the controller, so it can do its thing?
Maybe in calling directly the model ?
'use strict';
import { myTable } from '../db';
export default class MyController {
store = async (req, res, next) => {
await MyTable.create({ full: req.body.fullUrl});
res.redirect('/');
}
}

Creating a DB service in an MVC Express app

I am creating a Node / Express / Mongo app and would like to have an MVC-style file layout with a separate database service. I'd like my routes (controllers) to be fairly lean and simply call methods from elsewhere when I need to perform CRUD functions.
This great post had a lot of solid answers. I'll paste below the the answer from EddieDean that seems to be closest to what I'd like to do.
However, when I'm trying to reference the Users object defined in the mongo.js file in my someFile.js file, it comes back as undefined. I am pretty sure I am missing something obvious and would love to know what that is.
I can get a reference to the DB connection itself and call dbConn.Users.addUser(x) but I cannot directly call Users.addUser(x) as in the someFile.js example below.
This seems minor but I've spent too much time on this to not have an answer. Call it a knowledge vendetta.
Thanks.
mongo.js
const { MongoClient } = require('mongodb');
const config = require('./config');
const Users = require('./Users');
const conf = config.get('mongodb');
class MongoBot {
constructor() {
const url = `mongodb://${conf.hosts.join(',')}`;
this.client = new MongoClient(url, conf.opts);
}
async init() {
await this.client.connect();
console.log('connected');
this.db = this.client.db(conf.db);
this.Users = new Users(this.db);
}
}
module.exports = new MongoBot();
Users.js
class User {
constructor(db) {
this.collection = db.collection('users');
}
async addUser(user) {
const newUser = await this.collection.insertOne(user);
return newUser;
}
}
module.exports = User;
app.js
const mongo = require('./mongo');
async function start() {
// other app startup stuff...
await mongo.init();
// other app startup stuff...
}
start();
someFile.js
const { Users } = require('./mongo');
async function someFunction(userInfo) {
const user = await Users.addUser(userInfo);
return user;
}
What I did is simply put all my routes into start function. This isn't the best solution, but as starting point at least not the worst.
So whenever you need an access to DB from some js file, just put them into start, so the mongo could establish the connection first.
So I want get the DB instance in /routes/users file.
const express = require("express");
const mongo = require("./mongo");
const app = express();
const PORT = process.env.PORT || 3000;
(async function start() {
await mongo.init();
app.use("/users", require("./routes/user"));
})();
If someFile.js is required before the mongo.init method is called, the mongoBot instance will have been created, but Users will indeed be undefined. If you need to require someFile before the init method is called, you can move your destructured assignment to inside your someFunction method.

(Sequelize) Using A Model By Name Dynamically

I'm wondering if it is possible with Sequelize to create the needed models and then use one based on a value received from a request. I've tried different means to accomplish this but not finding a working solution. I accept it may not exist. But if so, does anyone have ideas on how to accomplish this?
const express = require('express');
const router = express.Router();
const Models = require('../models');
...
for (let modelName in tablesObj) {
if (modelName == primaryTable) {
Models.modelName.findAll()
.then(results => {
console.log(result);
});
}
}
The best way to do this is in your loader in the /models directory. It can export an object of Models keyed by the model name.
models/index.js
const fs = require('fs');
const path = require('path');
const sequelize = // your db connection
// object to hold all the models to export
const models = {};
// Read all the files from this dir and load the models
fs.readdirSync(__dirname)
.forEach((file) => {
if (file !== path.basename(__filename) && file.endsWith('.js')) {
const model = sequelize.import(
path.join(__dirname, '/', file.replace(/\.js$/, ''))
);
models[model.name] = model;
}
});
/* anything else you want to do goes here */
// export the models
module.exports = models;
Now you can load the models and then access each one by the name.
other.js
const models = require('../models');
async function runQuery() {
// access "modelName" model
const result = await models.modelName.findByPk(1);
// rest of your method
}
I usually take this a step further and load the models into the file that creates the Express app, then set the models to a property on the app and pass that around to routers.
server.js
const Express = require('express');
const models = require('./models');
const app = new Express();
app.model = (model) => models[model];
Now you can access your models like so:
// assuming the model name is "Widget"
const widget = await app.models('Widget').findOne(...);
For me, there was no need to add/modify the index.js file. The index.js was already present inside models folder by default.
Below code works for me!!,
const models = require("../../models");
let modeName, runningModel;
router.get('/api/:module', (req, res) => {
modelName = (req.params.module)
runningModel = models[modelName];
runningModel.findAndCountAll({
//your code goes here
})
.then(resutls => res.json(results)
.catch(err => res.json(err);
}
The following are the package and version used in my project
"express": "^4.17.1",
"morgan": "^1.10.0",
"pg": "^8.2.1",
"sequelize": "^5.21.4",
"sequelize-cli": "^5.5.1"

is there a way to create a separate file for mongodb operation functions and use those functions in index file?

i have main code in index.js file with express server, and i want to create another file just to handle database CRUD operations. How can i do the following?
index.js
import functions from mongo.js
getUsers()
mongo.js
using MongoClient.connect to connect to db
function getUser(){
// get users from mongo db
}
You can achieve this creating helper function:
I will explain on example from my project:
First you need to create your helper function file
helper.js
module.exports = {
userAuthenticated: function(req, res, next){
if(req.isAuthenticated()){
return next();
}
res.redirect('/login')
}
};
As you can see I exported this function.
Then we are able to reach the code from it like this in other .js files:
other.js
const {userAuthenticated} = require('../../helpers/authentication.js');
router.all('/*', userAuthenticated, (req, res, next)=>{
req.app.locals.layout = 'admin';
next();
});
Do you use mongoose? I strictly recommend to use this lib instead of native connection driver.
You could achieve your goal by creating mongoose connection in a separate file, then import connection instance to the file with db schema. For example:
const mongoose = require("./mongo");
const Schema = mongoose.Schema;
const User = new Schema({
name: { type: String },
password: { type: String })
const UserModel = mongoose.model("User",User);
module.exports = {
model: UserModel,
getById: id => UserModel.find({_id:id})
}
And then just import those file in the place where you want to use it:
const UserModel = require("./User");
...
UserModel.getById("someid").then(handler);

Best practices for building singletons in node.js

I am trying to build an express app and I need to create some singletons (like db object in Sequelizer).
app.js
app.use(...);
app.use(...);
var serviceLocator = {
foo: require('foo'),
bar: require('bar')
}; //object holding singletons.
app.use('/api/todo', new todoRoutes(serviceLocator));
todoRoutes.js
module.exports = (serviceLocator) => {
var router = express.Router();
router.get('/', (req,res,next) => {
//use serviceLocator.foo
});
router.get('/:id',(req,res,next) => {
//use serviceLocator.bar
});
};
Is this a good practice?
(I've also read about building singletons using require caching, but I have concerns since in the official docs they say that require "may not" return the same object).
How I usually do it looks something like this:
app.js
const db = require('./path/to/db/singleton');
const sql = require('./path/to/Sequelizer/singleton');
app.use(...);
app.use((req, res, next) => {
req.db = db;
next();
});
app.use((req, res, next) => {
req.sql = sql;
next();
});
app.use('/api/todo', require('./todoRoutes');
todoRoutes.js
const express = require('express');
const router = express.Router();
router.get('/', (req,res,next) => {
console.log(req.db);
});
router.get('/:id',(req,res,next) => {
console.log(req.sql);
});
module.exports = router;
The overall is just to add some middleware that adds it to the req, which is going to go through the pipeline for you. You could namespace it too by adding it to req.server.<thing> or something.
You can also do this to generate a request ID by bringing in uuid and attaching an ID to every req in another middleware that you can use in your log statements later, that way you can track requests through.
Alternatively, just require those singletons into the todoRoutes.js and use them directly. They're singletons on your server, so I don't see any issue with that.

Resources