(Sequelize) Using A Model By Name Dynamically - node.js

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"

Related

Trouble Deleting Object in Mongo Collection (Express / Monk)

I am trying and failing to delete an object from a mongo collection that I created. I can make get and post requests just fine, but I am totally lost as to how to delete an item from a given collection. I am currently just deleting the entire collection when trying to pass in the item by id...
Just so you can see what I am working with...
/**** IMPORTS *****/
const express = require('express');
const cors = require('cors');
const monk = require('monk');
const app = express();
const db = monk('localhost/att');
// Mongo Collection packs holds package objects
const packs = db.get('packages');
app.use(cors());
app.use(express.json());
And my back-end delete path:
//packs is my collection
app.delete('/packages/:id', (req, res) => {
packs.remove({"_id": req.body.id});
});
and my function on the front-end:
function listAllPackages() {
fetch(API_URL_PACKAGES).then(response => response.json()).then(packageObjs => {
// Access to all the package objects in DB here
// packageObjs : Array of Packages
let html = '';
packageObjs.forEach(p => {
html += `<option id="${p._id}" name="${p._id}">${p._id} - Name: ${p.name}, Price: ($)${p.price}, Duration: ${p.duration}(minutes)</option>`;
});
allPackages.innerHTML = html;
const delPackageDiv = document.querySelector('.deletePackage');
delPackageDiv.innerHTML = `<button id='deletePackageSubmit'>DELETE PACKAGE</button>`;
const delPackageButton = document.querySelector('#deletePackageSubmit');
delPackageButton.addEventListener('click', () => {
// GET SELECTED PACKAGE
const delForm = document.querySelector('.showPackages');
const opt = delForm.options[delForm.selectedIndex];
fetch(API_URL_PACKAGES + '/' + opt.id, {
method: 'DELETE'
});
});
});
}
Figured it out finally! Here's my solution in case anyone else is struggling like I was...
Change this:
//packs is my collection
app.delete('/packages/:id', (req, res) => {
packs.remove({"_id": req.body.id});
});
To this:
//packs is my collection
app.delete('/packages/:id', (req, res) => {
packs.remove({"_id": req.params.id });
});
Voila!

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.

TypeError: connectDb is not a function?

I am new to express and node while I connecting mongodb from mongoose to localhost I get an error like TypeError: connectDb is not a function?
src/models/index
const mongoose = require("mongoose");
const User = require("./user");
const Message = require("./message");
const connectDb = () =>
mongoose.connect(process.env.DATABASE_URL, { useNewUrlParser: true });
const models = { User, Message };
exports = { connectDb };
module.exports = models;
src/index
// .... Some code here
const { connectDb } = require("./models");
// .... Some code here
connectDb()
.then(async () => {
app.listen(process.env.PORT);
})
.catch(error => console.error(error));
exports = { connectDb };
this statement is not correct. Its not exporting the connectDb function.
if you want to use exports keyword to export connectDb function, you have to do it as
exports.connectDb = () => mongoose.connect(process.env.DATABASE_URL, { useNewUrlParser: true });
or simply
exports.connectDb = connectDb;
If you want to export multiple things, you can add multiple properties to exports object
to export models, you can do
exports.models = models;
Now this file will export an object that has two properties on it, connectDb and models
Another way to export multiple things is
module.exports = {
connectDb,
models
};
this will also export an object containing two properties, connectDb and models
You are actually doing a weird thing. exports variable is set to point to module.exports, so when u change its reference by doing exports = { connectDb } you are actually breaking that variable's reference, so it's not exporting anything. Next thing u do is exporting ur models, which is correct, but your connectDb is actually not being exported, so you can not use it in your second file. I guess you want to export both, so actually there are some ways to achieve that. Here you have two valid options, first one using the spread operator with module.exports and second one using exports and not changing its reference but it's properties, so it keeps working correctly:
module.exports = {
...models,
connectDb
}
exports.connectDb = connectDb
exports.User = User
exports.Message = Message

How to reuse the same sequelize connection anywhere in my Express app?

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);
})

Dynamically load routes with express.js

I am using express.js as a webserver and would like an easy way to separate all the "app.get" and "app.post" functions to separate files. For example, if I would like to specify get and post functions for a login page, I would like to have a login.js file in a routes folder that is dynamically loaded (will automatically add all of the files without having to specify each one) when I run node app.js
I have tried this this solution!, but it isn't working for me.
app.js
var express=require("express");
var app=express();
var fs=require("fs");
var routePath="./routers/"; //add one folder then put your route files there my router folder name is routers
fs.readdirSync(routePath).forEach(function(file) {
var route=routePath+file;
require(route)(app);
});
app.listen(9123);
I have put below two routers in that folder
route1.js
module.exports=function(app){
app.get('/',function(req,res){
res.send('/ called successfully...');
});
}
route2.js
module.exports=function(app){
app.get('/upload',function(req,res){
res.send('/upload called successfully...');
});
}
Typescript
routes/testroute.ts
import { Router } from 'express';
const router = Router();
router.get('/test',() => {
// Do your stuffs Here
});
export = router;
index.ts
let app = express()
const routePath = path.join(__dirname, 'routes');
fs.readdirSync(routePath).forEach(async (filename) => {
let route = path.join(routePath, filename);
try {
const item = await import(route);
app.use('/api', item.default);
} catch (error) {
console.log(error.message);
}
});
app.listen()
I ended up using a recursive approach to keep the code readable and asynchronous:
// routes
processRoutePath(__dirname + "/routes");
function processRoutePath(route_path) {
fs.readdirSync(route_path).forEach(function(file) {
var filepath = route_path + '/' + file;
fs.stat(filepath, function(err,stat) {
if (stat.isDirectory()) {
processRoutePath(filepath);
} else {
console.info('Loading route: ' + filepath);
require(filepath)(app, passport);
}
});
});
}
This could be made more robust by checking fro correct file extensions etc, but I keep my routes folder clean and did not want the added complexity
With this approach, there is no need to write routes manually. Just setup a directory structure like the URL paths. Example route is at /routes/user/table/table.get.js and API route will be /user/table.
import app from './app'
import fs from 'fs-readdir-recursive'
import each from 'lodash/each'
import nth from 'lodash/nth'
import join from 'lodash/join'
import initial from 'lodash/initial'
const routes = fs(`${__dirname}/routes`)
each(routes, route => {
let paths = route.split('/')
// An entity has several HTTP verbs
let entity = `/api/${join(initial(paths), '/')}`
// The action contains a HTTP verb
let action = nth(paths, -1)
// Remove the last element to correctly apply action
paths.pop()
action = `./routes/${join(paths, '/')}/${action.slice(0, -3)}`
app.use(entity, require(action))
})
Example route:
import { Router } from 'express'
import Table from '#models/table.model'
const routes = Router()
routes.get('/', (req, res, next) => {
Table
.find({user: userIdentifier})
.select('-user')
.lean()
.then(table => res.json(table))
.catch(error => next(error))
})
module.exports = routes

Resources