nodejs: Access variables in routes - node.js

Here is how my routes.js looks like:
module.exports = function (server, db) {
// Deals
const deals = require('./controllers/deals');
server.get('/deals', deals.findAll);
server.get('/deals/:id', deals.findById);
server.post('/deals', deals.add);
server.put('/deals/:id', deals.update);
server.del('/deals/:id', deals.delete);
}
As you can see, there are server and db variables here. However, I'm not sure how could I pass these two variables to deals.findAll, and other REST methods.
Here is how deals.findAll method looks:
const findAll = (req, res, next) => {
// I need to access db here, so I can use Sequelize.
}
exports.findAll = findAll
The reason I need db is Sequelize ORM.

You can use Function.prototype.bind() to do this:
module.exports = function (server, db) {
// Deals
const deals = require('./controllers/deals');
server.get('/deals', deals.findAll.bind(null, server, db));
server.get('/deals/:id', deals.findById.bind(null, server, db));
server.post('/deals', deals.add.bind(null, server, db));
server.put('/deals/:id', deals.update.bind(null, server, db));
server.del('/deals/:id', deals.delete.bind(null, server, db));
}
The Function.prototype.bind() method prepends those arguments, so you need to modify the parameters of those functions to:
const findAll = (server, db, req, res, next) => {
// server & db is set here
}

Related

How do I get the full route path including the parameters from express or extend the request object to do so?

I have the following route in my express (version 4.17.1) API in a postTimecardCompany.js file:
const mongoose = require('mongoose');
const Timecard = require('./../models/timecard');
function postTimecardCompany(server) {
server.post('/api/v1/timecard/:userId', (req, res) => {
// Insert timecard data into the database
Timecard.create(data, (error, result) => {
// Check for errors
if (error) {
res.status(500).end();
return;
}
// Respond
res.status(200).send({timecardId: result._id});
});
});
}
module.exports = postTimecardCompany;
The route (among other routes) is loaded via the following mechanism by server.js file:
[
'postTimecardCompany',
'anotherRoute',
'someOtherRoute',
'andSoOn...'
].map((route) => {
require('./core/routes/' + route + '.js').call(null, server)
});
I have a middleware (in server.js file) where I check which route is being called.
server.use((req, res, next) => {
// If route is "/api/v1/timecard/:userId" do something
});
I have found various solutions which do nearly what I am looking for, but not exactly.
For example, if I post to the route with a data parameter userId value of "123f9b" then req.originalUrl gives an output of "/api/v1/timecard/123f9b."
What i'm looking to get is the original route path with the parameters in it so for a request of "/api/v1/timecard/123f9b" it would be: "/api/v1/timecard/:userId."
How do I get this functionality in express or extend express to get the original route path with parameters in the request object?
if you want to use from your approach, it's is impossible, after that your approach is not standard in express check the documentation, if you want get routes in a middleware you should try like this:
server.js
const express = require('express')
const server = express()
const postTimecardCompany = require('./routes/postTimecardCompany.js')// don't use map()
server.use("/",postTimecardCompany)//use the routes
server.listen(6565,()=>console.log(`Listening to PORT 6565`))
routes of postTimecardCompany.js
use Router of express and export router, and you can use middleware before each route you want, there are many ways to use middleware in routes, check the documentation
const express = require("express");
const router = express.Router();
const middleware = require('../middleware');//require middlewares
router.post("/api/v1/timecard/:userId", middleware,(req, res) => {
// Insert timecard data into the database
console.log(req.route.path);
});
module.exports = router;
middleware.js
module.exports = ((req, res, next) => {
console.log(req.route.path);
next()
});

Passing a mysql connection to expression routing middleware function as argument

I am wondering if I can pass a connected database instance to an express middleware as an argument.
For example:
// in app.js
const mysql = require('mysql');
const mysqlCon = mysql.createConnection({
// some configurations...
})
mysqlCon.connect((err)=> if(err) throw err);
then in a separate file...
// in a separate file called: login handler
const loginHandler = (req, res, dbCon) => {
// query database and stuff...
}
module.exports = loginHanlder;
then back in app.js passes the established mysql connection into the middleware handler
// back in app.js
// a route
const loginHandler = require('./handlers/loginHanlder.js');
app.get('/login', loginHanlder(req, res, mysqlCon));
Will this work? Or are there a conventionally better way of achieving this goal?
The way you're trying to do it won't work, as req,res will be undefined.
However, you could create a factory function that takes the connection as a parameter and retours a route-handler function. Since the function that will be returned is a closure, it will have access to the db-connection in the parent scope. Something like:
function getRequestHandler(dbConn) {
return function (req, res, next) {
// use dbConn here to do stuff
}
}
You can use it like this:
const dbConn = initConnection(); // connect to your db here and return the connection instance
app.get('/login', getRequestHandler(dbConn));
Alternatively, if you don't want/need to create your own factory function, you can simply do:
app.get('/login', (req, res) => loginHandler(req, res, mysqlCon));

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.

Exporting a Mongoose connection that becomes available in an async callback?

I'm new to coding in Node.js and I've created a DB connection through mongoose, then exporting the database object into my main app.js file:
// db.js
var mongoose = require("mongoose");
var uri = /*The URL*/;
var db = mongoose.connect(uri, {
useMongoClient: true
});
exports.db = db;
Here I'm trying to use that DB connection to perform CRUD operations:
// app.js
var db = require('./db');
app.post('/', function (req, res) {
db.collection(/* … */); // throws error
});
The error is:
TypeError: db.collection is not a function
I think this is because that the call of connect in the db.js is asynchronous and the require in app.js is synchronous - so it'll be undefined when I execute db.collection?? How can Mongoose be used to avoid this error?
Goal: I want to be able to call db.collection.insertOne() within my app.js
I found a somewhat related topic but it doesn't use Mongoose, so I'm confused as to how to resolve this issue: How to export an object that only becomes available in an async callback?
Answering my own question:
It'll need a mongoose model in the app.js. So instead of doing db.collection in the main app.js, I ended up routing the CRUD operations to functions defined in the controller file.
//app.js
var app = express();
var router = express.Router();
app.use(router);
require("./Routes")(router);
router.get("/xxx", xxx.get);
Here are the routes:
//routes.js - takes care of routing URLs to the functions
var XXX = require('../xxx');
module.exports = function(router){
router.get('/XXX', XXX.get)
};
Here is the actual function:
//xxx.js - functions defined here
exports.get = function (req, res) {
XXX.getAll(req.body, function(err, result){
if (!err) {
return res.send(result); // Posting all the XXX
} else {
return res.send(err); // 500 error
}
});
};

How do I combine Connect middleware into one middleware?

I have a few middlewares that I want to combine into one middleware. How do I do that?
For example...
// I want to shorten this...
app.use(connect.urlencoded())
app.use(connect.json())
// ...into this:
app.use(combineMiddleware([connect.urlencoded, connect.json]))
// ...without doing this:
app.use(connect.urlencoded()).use(connect.json())
I want it to work dynamically -- I don't want to depend on which middleware I use.
I feel like there's an elegant solution other than a confusing for loop.
Express accepts arrays for app.use if you have a path:
var middleware = [connect.urlencoded(), connect.json()];
app.use('/', middleware)
However, if you want a generic combineMiddleware function, you can build a helper easily without any additional libraries. This basically takes advantage of the fact that next is simply a function which takes an optional error:
/**
* Combine multiple middleware together.
*
* #param {Function[]} mids functions of form:
* function(req, res, next) { ... }
* #return {Function} single combined middleware
*/
function combineMiddleware(mids) {
return mids.reduce(function(a, b) {
return function(req, res, next) {
a(req, res, function(err) {
if (err) {
return next(err);
}
b(req, res, next);
});
};
});
}
If you like fancy stuff, here is one of possible solutions:
var connect = require('connect')
var app = connect()
function compose(middleware) {
return function (req, res, next) {
connect.apply(null, middleware.concat(next.bind(null, null))).call(null, req, res)
}
}
function a (req, res, next) {
console.log('a')
next()
}
function b (req, res, next) {
console.log('b')
next()
}
app.use(compose([a,b]))
app.use(function (req, res) {
res.end('Hello!')
})
app.listen(3000)
Here is what it does: compose function takes array of middleware and return composed middleware. connect itself is basically a middleware composer, so you can create another connect app with middlewares you want: connect.apply(null, middleware). Connect app is itself a middleware, the only problem is that it doesn't have a next() call in the end, so subsequent middleware will be unreachable. To solve that, we need another last middleware, which will call next : connect.apply(null, middleware.concat(last)). As last only calls next we can use next.bind(null, null) instead. Finally, we call resulting function with req and res.
Old question, but the need is still frequent for all the things using middlewares, like connect, express or custom made req/res/next patterns.
This is a very elegant and purely functional solution:
File ./utils/compose-middleware.js:
function compose(middleware) {
if (!middleware.length) {
return function(_req, _res, next) { next(); };
}
var head = middleware[0];
var tail = middleware.slice(1);
return function(req, res, next) {
head(req, res, function(err) {
if (err) return next(err);
compose(tail)(req, res, next);
});
};
}
module.exports = compose;
The final result of the compose(middlewareList) is a single middleware that encapsulates the whole chain of middleware initially provided.
Then simply import it and use like this:
File app.js:
var connect = require('connect');
var compose = require('./utils/compose-middleware');
var middleware = compose([
connect.urlencoded(),
connect.json()
]);
var app = connect();
app.use(middleware);
A simple and native way, and you don't need to install anything.
const {Router} = require('express')
const combinedMiddleware = Router().use([middleware1, middleware2, middleware3])
Then you can use the combinedMiddleware where you want. For example, you may want to run different set of middlewares/handlers for the same route depending on some conditions (a request attributes, for example):
app.get('/some-route', (req, res, next) => {
req.query.someParam === 'someValue'
? combinedMiddleware1(req, res, next)
: combinedMiddleware2(req, res, next)
})
If you're willing to use a library:
https://www.npmjs.org/package/middleware-flow
var series = require('middleware-flow').series;
var app = require('express')();
app.use(series(mw1, mw2, mw2)); // equivalent to app.use(mw1, mw2, mw3);
Make a list and use a loop.
const connect = require('connect')
const { urlencoded, json } = require('body-parser')
const app = connect()
[ urlencoded(), json() ].forEach(app.use, app)
The second argument of .forEach is used for this, but if you like you can also do the same with:
[ urlencoded(), json() ].forEach(app.use.bind(app))

Resources