Warning! We're going to see some Functional Programming concepts like pure functions, side effects etc. and obviously higher order functions.
Recap
In the last post, we saw how we can create a middleware wrapper that can wrap any function and make it an express middleware.
const withMiddleware = (func) => {
return (req, res, next) => {
func();
next();
}
}
module.export = withMiddleware;
And we can use it in this way
const withMiddleware = require('./withMiddleware');
const funcOne = () => {
}
const funcTwo = () => {
}
const middlewareOne = withMiddleware(funcOne);
const middlewareTwo = withMiddleware(funcTwo);
app.get("/path", middlewareOne, middlewareTwo, (req, res)=>{
res.send({});
})
We had a few unanswered questions
- Passing data between middlewares? Although we solved it in the first post, see the PS: section, but how can we make it work effortlessly.
- What if I'm writing auth or validation middlewares, that needs access to the request object?
- How will we handle the async middlewares?
- Logging. The generic activities of the middlewares needs to be logged. Like, in case of async middleware, that fetches data over another API, or fetches data from a DB, the time taken for the async operation needs to be logged.
Let's get started...
1. Passing data between middlewares
The most effortless way of passing data between middlewares is using the res.locals object. We're going to use the same.
const withMiddleware = (func) => {
return (req, res, next) => {
const response = func();
if (response) {
res.locals[`${func.name}Response`] = response;
}
next();
}
}
module.export = withMiddleware;
and when we finally use it
const withMiddleware = require('./withMiddleware');
const funcOne = () => {
return true;
}
const funcTwo = () => {
return true;
}
const middlewareOne = withMiddleware(funcOne);
const middlewareTwo = withMiddleware(funcTwo);
app.get("/path", middlewareOne, middlewareTwo, (req, res)=>{
const {funcOneResponse, funcTwoResponse} = res.locals;
if(funcOneResponse && funcTwoResponse){
res.send("Aal izz well");
} else {
res.status(400).send('Bad Request')
}
})
2. Access to request and response object
Ok, now we need to give access to the request object. Lets make one more modification to our Higher order function.
const withMiddleware = (func) => {
return (req, res, next) => {
const response = func(req, res);
if (response) {
res.locals[`${func.name}Response`] = response;
}
next();
}
}
module.export = withMiddleware;
And how are we going to use it?
const withMiddleware = require('./withMiddleware');
const funcOne = (req, res) => {
if(req.body.username && req.body.password){
return true;
} else {
return false;
}
}
const funcTwo = () => {
return true;
}
const middlewareOne = withMiddleware(funcOne);
const middlewareTwo = withMiddleware(funcTwo);
app.post("/path", middlewareOne, middlewareTwo, (req, res)=>{
const {funcOneResponse, funcTwoResponse} = res.locals;
if(funcOneResponse && funcTwoResponse){
res.send("Aal izz well");
} else {
res.status(400).send('Bad Request')
}
})
What if my function is an async function
Well, there is no easy answer to this. We need a different Higher Order Function to handle such cases
const withMiddleware = (func) => {
return (req, res, next) => {
const response = func(req, res);
if (response) {
res.locals[`${func.name}Response`] = response;
}
next();
}
}
const withAsyncMiddleware = (func) = {
return async (req, res, next) => {
const response = await func(req, res);
if (response) {
res.locals[`${func.name}Response`] = response;
}
next();
}
}
module.export = { withMiddleware, withAsyncMiddleware };
And we can use this in the following manner
const { withMiddleware, withAsyncMiddleware } = require('./withMiddleware');
const funcOne = (req, res) => {
if(req.body.username && req.body.password){
return true;
} else {
return false;
}
}
const funcTwo = async () => {
const data = await apiResponse();
return data;
}
const middlewareOne = withMiddleware(funcOne);
const middlewareTwo = withAsyncMiddleware(funcTwo);
app.post("/path", middlewareOne, middlewareTwo, (req, res)=>{
const {funcOneResponse, funcTwoResponse} = res.locals;
if(funcOneResponse && funcTwoResponse){
res.send(funcTwoResponse);
} else {
res.status(400).send('Bad Request')
}
})
4. Now the easiest part. how we log the time for the async function?
Simple
const withMiddleware = (func) => {
return (req, res, next) => {
const response = func(req, res);
if (response) {
res.locals[`${func.name}Response`] = response;
}
next();
}
}
const withAsyncMiddleware = (func) = {
return async (req, res, next) => {
const t1 = Date.now();
const response = await func(req, res);
const t2 = Date.now();
console.log(`Time Taken to execute async ${func.name} is ${t2 - t1}`);
if (response) {
res.locals[`${func.name}Response`] = response;
}
next();
}
}
module.export = { withMiddleware, withAsyncMiddleware };
That's all for now,
Thanks