Chaining of middleware in Express - javascript

I am writing APIs and wanted to understand what is a good way to add middleware shared by multiple routes. The middlewares does the same things in each route, like validating hosts/ip, validate user, etc.
The req object gets loaded with other objects in each of the middlewares like req.host, req.ip, req.username etc.
app.post("/route1", middleware1, middleware2, middleware3, middleware4);
app.post("/route2", middleware1, middleware2, middleware3, middleware4);
const middleware1 = (req, res, next) => {
// does something to validate user
req.username = "username"
next();
}
const middleware2 = (req, res, next) => {
// host validation
req.host = "something modified in validation"
next();
}
const middleware3 = (req, res, next) => {
// checks for mac and ip
req.mac = "mac addr"
next();
}
const middleware4 = (req, res, next) => {
res.send();
}
Or something like this:
app.post("/route1", middleware1);
app.post("/route2", middleware1);
const middleware1 = (req, res) => {
// does something to validate user
req.username = "username"
middleware2(req, res);
}
const middleware2 = (req, res) => {
// host validation
req.host = "something modified in validation"
middleware3(req, res);
}
const middleware3 = (req, res) => {
// checks for mac and ip
req.mac = "mac addr"
middleware4(req, res);
}
const middleware1 = (req, res) => {
res.send();
}
Thanks.

Generally I wouldn't call middlewares directly from another middleware. It mixes responsibilities of middleware logic and where the middleware is used.
Express is much more configurable than you think though. You can also install common middlewares in common paths:
If all routes use the middlewares:
// How common middlewares are normally installed:
app.post(middleware1);
app.post(middleware2);
app.post(middleware3);
app.post(middleware4);
// Alternative, less common way to do it:
app.post(middleware1,middleware2,middleware3,middleware4);
If only a specific pattern of urls use the middlewares:
// Use a regexp:
app.post(/route(1|2)/, middleware1, middleware2, middleware3, middleware4);
// Or if you don't like regexp, use globs:
app.post('route*', middleware1, middleware2, middleware3, middleware4);
// Or a more specific glob pattern:
app.post('route1?2?', middleware1, middleware2, middleware3, middleware4);
If all url in a subpath use the middlewares. For example, lets say if all urls in /route/... use the middlewares:
const route = express.Router();
app.use('/route',route);
route.post(middleware1);
route.post(middleware2);
route.post(middleware3);
route.post(middleware4);
If none of the above appeal to you you can still use your second option but instead of calling middlewares inside each other you write a middleware to initialize middlewares:
function commonMiddlewares (req, res, next) {
middleware1(req,res,function() {
middleware2(req,res,function() {
middleware3(req,res,function() {
middleware4(req,res,next);
});
});
});
}
Which can be written in a less nested way:
function commonMiddlewares (req, res, next) {
function runMiddleware4 () {
middleware4(req,res,next);
}
function runMiddleware3 () {
middleware3(req,res,runMiddleware4);
}
function runMiddleware2 () {
middleware2(req,res,runMiddleware3);
}
middleware1(req,res,runMiddleware2);
}

const express = require('express')
const { routesMiddleware } =require('./middlewares')
const { pureVaidationsFunctions1 } =require('./services')
const rout1 =express.Router()
const rout2 =express.Router()
const app = express()
app.use('/route1',route1)
app.use('/route2',route2)
// routesMiddleware a middleware to handle the execution of list of functions
// pureVaidationsFunctions1 list of funtions that `routesMiddleware` will consume
route1.post(routesMiddleware(pureVaidationsFunctions1))
route2.post(routesMiddleware(pureVaidationsFunctions2))
make sense?

You can specify multiple middlewares, see the app.use docs:
An array of combinations of any of the above.
You can create a file of all middlewares like -
middlewares.js
module.exports = [
function(req, res, next){...},
function(req, res, next){...},
function(req, res, next){...},
.
.
.
function(req, res, next){...},
]
and as then simply add it like:
/*
you can pass any of the below inside app.use()
A middleware function.
A series of middleware functions (separated by commas).
An array of middleware functions.
A combination of all of the above.
*/
app.use(require('./middlewares.js'));
Note - Do this only for those middlewares which will be common for all such requests.

Related

Express: why middlewares doesn't work properly for independent routers?

I have the following code with 3 independent routers
const Express = require("express")
const app = Express()
const usersRouter = Express.Router()
const productsRouter = Express.Router()
const storeRouter = Express.Router()
productsRouter.use((_, res, next) => {
res.send("products fail")
//next()
})
storeRouter.use((_, res, next) => {
res.send("store fail")
//next()
})
usersRouter.route("/users")
.get((_, res) => res.send("users"))
productsRouter.route("/products")
.get((_, res) => res.send("products"))
storeRouter.route("/store")
.get((_, res) => res.send("store"))
app.use(usersRouter)
app.use(productsRouter)
app.use(storeRouter)
app.listen(80, () => console.log("running"))
But every time I request /store route it pass through productRouter middleware which is assigned only to it.
I can't understand this behavior.
Why is this?
How can I manage independent middlewares for each one?
GET /store 200
products fail
Expected
GET /store 200
store fail
When you do this:
app.use(productsRouter)
that sends ALL requests to the productsRouter and thus its middleware runs for all requests. So, when you have this:
productsRouter.use((_, res, next) => {
res.send("products fail")
//next()
});
That will run on every single request.
If you want the router to only see certain requests, then register the router on a path instead so the router only gets requests destined for a certain path.
app.use("/products", productsRouter)
And, then remove the path itself from the router's routes since the path will have already been filtered.
In order to achieve the expected behavior, you will have to make little changes to your code.
First:
Take this approach, since it will allow you to keep everything clean and separated (this is crucial if you want to implement specific middlewares for each route).
usersRouter.
.get("/", (req, res) => res.send("users"))
productsRouter.route
.get("/", (req, res) => res.send("products"))
storeRouter.route("/store")
.get("/", (req, res) => res.send("store"))
app.use("/users", usersRouter)
app.use("/products", productsRouter)
app.use("/store", storeRouter)
Instead of this one
usersRouter.route("/users")
.get((_, res) => res.send("users"))
productsRouter.route("/products")
.get((_, res) => res.send("products"))
storeRouter.route("/store")
.get((_, res) => res.send("store"))
app.use(usersRouter)
app.use(productsRouter)
app.use(storeRouter)
Second:
Uncomment the next() call on your middlewares, identify the request parameter on their callbacks and store them in variables (not crucial, but improves readability)
const productsMiddleware = (req, res, next) => {
res.send("products fail")
next()
}
const storeMiddleware = (res, res, next) => {
res.send("store fail")
next()
}
Third:
Pass the middleware you want to apply to a specific controller right after the route and before the actual controller declaration on your router. E.G.
usersRouter.
.get("/", (req, res) => res.send("users"))
productsRouter.route
.get("/", productsMiddleware, (req, res) => res.send("products"))
storeRouter.route("/store")
.get("/", storeMiddleware, (req, res) => res.send("store"))
By doing all this things, you'll end up with "independent middlewares" that only apply to the specified route/controller.

Express Router with Decision

Routing With Middleware checks based req.query
how to choose the next() based on req.query property
Example:
const preQueryCheck = (req,res,next)=>{
//??
}
Router.route(‘/add/:id’).post(preQuerycheck,addsum)
What I want is if req.query = type:sum
I want it to go to addsum
If it’s subtract it shd go to subtract !!
Is it possible ?
Here is example of writing your own middleware:
var express = require('express')
var app = express()
var myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
app.use(myLogger)
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000)
So in this case the middleware function is myLogger!

How to automate next() call in every route function? (express.js)

Hi I am facing the problem that I need to log each incomming request and the associated responses in my database. My current solution looks like the following:
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.status(200).send(req.allCustomers);
next(); // <- this is my personal problem
});
./middleware/logging.js
module.exports = function (req, res, next) {
db.query(
`INSERT INTO logging SET ?`,
{
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(req.response)
}
);
}
routes declaration
module.exports = function(app) {
app.use(express.json());
app.use('/api/customers', customers); // <- ROUTE ./routes/customer.js
app.use(logging); // <- MIDDLEWARE ./middleware/logging.js
}
I already mentioned my problem in my first piece of code. It is really repetitive to call next() in every route manually and I would like to avoid this. I already tried to load the middleware before all routes, call next() in the middleware function and execute my db query afterwards but I do not have the response at this point because of the async functionality.
Is there any way to handle this situation or will I need keep calling next() at the end of each route function?
If you don't want to call next() from your routes, you cannot have middleware run after them. It needs to be placed before. But can you get the response inside a middleware that runs before the route? The answer is yes!
It may be a little hacky, but since your route uses res.send(), you can use that to your advantage. By running before your route, your middleware can hijack that res.send function, to make it do other stuff.
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.send(req.allCustomers); // We'll hijack this
});
./middleware/logging.js
module.exports = function (shouldBeLoggedFunc) {
return function (req, res, next) {
if (shouldBeLoggedFunc(req)) {
// Store the original send method
const _send = res.send;
// Override it
res.send = function (body) {
// Reset it
res.send = _send;
// Actually send the response
res.send(body);
// Log it (console.log for the demo)
console.log(`INSERT INTO logging SET ?`, {
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(body)
});
};
}
next();
};
};
routes declaration
function shouldBeLogged(req) {
// Here, check the route and method and decide whether you want to log it
console.log(req.method, req.path); // e.g. GET /api/customers
return true;
}
module.exports = function(app) {
app.use(express.json());
app.use(logging(shouldBeLogged)); // <- Place this before your routes
app.use('/api/customers', customers);
};
when you use express.Router class like you already did and then use this code
app.use('/api/customers', customers);
you don't have to write 'next()' inside callback function in router.get .
there is an example
create a router file named birds.js in the app directory, with the following content:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
Then, load the router module in the app:
var birds = require('./birds')
// ...
app.use('/birds', birds)

Express router matching params

Assuming I have two routes one with params, one without:
/foo?bar
/foo
I want to use two different handlers for these two routes. I know I can do something like this.
app.use('/foo', (req, res) => {
if (req.params.foo !== undefined) {
// do something
} else {
// do something else
}
})
But, it would make the code harder to read. Is there a way to match a route that has a parameter? I would like to manage this situation:
app.use('/foo', x);
app.use('/foo?bar', y);
As far as I know, queries can not be filtered on use handler.
Instead, I made out with the very similar situation by using next.
app.use('/foo', (req, res, next) => {
if (req.query.foo !== undefined) return next();
//if foo is undefined, it will look for other matching route which will probably the next '/foo' route
/* things to do with foo */
});
app.use('/foo', (req, res) => {
//things to without foo
});
https://expressjs.com/en/guide/using-middleware.html
this document may also help you
How about this?
const express = require('express');
const app = express();
// curl -X GET http://localhost:3000/foo
app.get('/foo', function (req, res, next) {
res.send('This is foo');
});
// curl -X GET http://localhost:3000/foo/bar
app.get('/foo/:?bar', function (req, res, next) {
res.send('This is foo with bar');
});
app.listen(3000);

Choosing which middleware to run based on request in Express JS

I would like to know how to choose between two different middleware functions, depending on the request for the endpoint. It could look something like this:
router.post("/findAvailableAgents", middleware1 || middleware2, (req, res) => {
// endpoint body
})
You could use another middleware which decides whether to choose middleware1 or middleware2
const decideMiddleware = (req, res, next) => {
if(condition) {
return middleware1(req, res,next)
} else {
return middleware2(req, res,next)
}
}
And use it in your code
router.post("/findAvailableAgents", decideMiddleware, (req, res))
There is two ways of achieve optional middleware behaviour:
1) Create another middleware, that checks condition and then passes all the parameters into the desired middleware. Example:
const middlewareStrategy = (req,res,next) => {
if(req.body.token1){
return middleware1(req,res,next);
}
return middleware2(req,res,next);
};
router.post("/findAvailableAgents", middlewareStrategy, handler);
2) Make middleware logic execution in a condition-driven manner. Example:
const middleware1 = (req,res,next) => {
if(req.body.token){
// execute some logic, then
return next();
}
// skip this middleware
next();
};
router.post("/findAvailableAgents", middleware1, middleware2, handler);
Now you can add multiple middleware using a below peice of code
app.get('/',[middleware.requireAuthentication,middleware.logger], function(req, res){
res.send('Hello!');
});

Categories

Resources