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));
Related
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.
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
}
i'd like to nest resources like the below eg.
/// fileA.js
app.use('/dashboards/:dashboard_id/teams', teams);
[...]
///teams.js
router.get('/', function(req, res, next) {
[...]
}
but I can't get the req.params["dashboard_id"] parameter in teams because it seems it is already substituted with the parameter value.
I've tried to baypass the problem by calling an intermediate function and pass somewhere the parameter but I can't figure out where ...
Do you have an answer?
Thanks,
Franco
You may try this solution:
//declare a function that will pass primary router's params to the request
var passPrimaryParams = function(req, res, next) {
req.primaryParams = req.params;
next();
}
/// fileA.js
app.use('/dashboards/:dashboard_id/teams', passPrimaryParams);
app.use('/dashboards/:dashboard_id/teams', teams);
///teams.js
router.get('/', function(req, res, next) {
var dashboardId = req.primaryParams['dashboard_id']; //should work now
//here you may also use req.params -- current router's params
}
Using Express 4.0.0 or above at the time of this writing:
To make the router understand nested resources with variables you will need create and bind a new router using app.use for every base path.
//creates a new router
var dashboardRouter = express.router();
//bind your route
dashboardRouter.get("/:dashboard_id/teams", teams);
//bind to application router
app.use('/dashboards', dashboardRouter);
This way Express will see the first part of the path and go to the /dashboards route, which has the :dashboard_id/teams path.
You can use the mergeParams option here
// fileA.js
app.use('/dashboards/:dashboard_id/teams', teams);
// teams.js
const router = express.Router({ mergeParams: true })
router.get('/', function(req, res, next) {
// you will have access to req.params.dashboard_id here
}
You can also see this answer: Rest with Express.js nested router
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))
I have very simple node.js noob question. How do I pass a variable to an exported route function?
Routes file
exports.gettop = function(n, req, res) {
console.log(n);
res.send(200);
};
Server file
app.get('/api/v1/top100', routes.gettop(100));
Error: .get() requires callback functions but got a [object Undefined]
For your example, you want to create a new function that will close around your value of n. In your case, you are executing gettop and passing the returned value to express as your route, which means gettop needs to return the route handler.
exports.gettop = function(n){
return function(req, res) {
console.log(n);
res.send(200);
};
};
As your code looks like you're using express you can use express app locals and express result locals to pass variables to your route. While the other answers propose working solutions I think that it's less obtrusive to use express mechanisms to set these variables.
With response locals (See Express API reference) you first have to set a variable somewhere in a middleware or route. I'll show the middleware approach
app.use(function(req,res, next) {
res.locals.top = 200;
next();
});
then in your route you can access this property via res.locals.variablename
exports.gettop = function(req, res) {
console.log(res.locals.top);
res.send(200);
};
In case you want to make these settings application wide a better approach is to use app locals (See Express API reference)
To set an app locals variable you can use
app.locals.top = 100;
To access this variable from your route use
exports.gettop = function(req, res){
console.log(req.app.locals.top);
res.send(200);
};
As an alternative to loganfsmyth's (very valid!) solution, you could leave your gettop function as-is and create a partial function:
app.get('/api/v1/top100', routes.gettop.bind(null, 100));
Try
app.post('/find_user',
require('./naas/autentication'),
require('./naas/authorization')(paramForRouter),
require('./routes/users'));
Where
require('./naas/autentication') is for example
module.exports = function (req, res, next) {next();}
And require('./naas/authorization')(paramForRouter) is
module.exports = function (paramForRouter) {
return function (req, res, next) {
this.param = paramForRouter;
console.log("Param value",this.param);
next();
};
};