Express JS passing middleware through controller function - javascript

I wanted to know if it is possible to pass middleware in the controller function
routes.js
const router = require('express').Router();
router.get('/home', home.get);
home.js
module.exports.get = (req, res) => {
res.send("Welcome to HOME");
};
I want to pass middleware in the get function of home.js, one method I know is to call the middleware function and then pass the controller function as next callback
module.exports.get = (req, res) => {
authCheckMiddleware(req, res, () => {
res.send("Welcome to HOME");
})
};
If there any approach? to pass the middleware in the function defination or something for home.js

You can do it by adding your middleware function as an argument preceding the actual controller:
router.get('/home', authCheckMiddleware, home.get);

Related

Execute middleware on every route call

In my Express application I implement routes in routes.ts:
var Router = express.Router();
Router.route('/models/:modelId')
.get(function (req, res) {
service.setParameter(req)
service.get(req,res)
});
Router.route('/models/:modelId')
.post(function (req, res) {
service.setParameter(req)
service.post(req,res)
});
And express.ts:
export const App = express()
App.use(express.json())
App.use(express.urlencoded({ extended: true }))
App.use(helmet())
App.use('/', Router)
At each router call I'd like to execute a piece of code service.setParameter(req) that gets particular parameter from 'params', but I don't want to add to each router method explicitly.
I tried adding it at as middleware before and after Router
App.use('/', Router)
App.use(function(req, res, next){
service.setParameter(req)
next()
})
But if I define it before Router then route hasn't been set yet, and I don't get the parameter I want, and if I define it after, then middleware is not executed.
How can execute service.setParameter(req) in a generic way so that it applies to all the routes?
In express.ts file, you can add a middleware that would do it before mounding the Router, and then just procced forward with next(). You can do it like this:
App.use('/*', (req, res, next) => {
service.setParameter(req);
next();
});
App.use('/', Router)
You need to place your custom middleware between the context path and your router inside app.use(..):
const router = express.Router();
router.post('/', (req, res) => {
service.post(req,res);
});
router.get('/', (req, res) => {
service.get(req,res)
});
app.use('/models', (req, res, next) => {
service.setParameter(req);
next();
}, router);
With above code the middleware will be excecuted for all requests to '/models'.
You can use app.use(async (req,res,next) => {...}) in order to declare a middleware that executes in all the requests, if you want this middleware to be called first, it must be declare before than your routes, the middleware have to call next() in order to continue with the execution flow, if you want to be called at the end of you request, you have to put at the end of your declarations but before of the error middleware, in that approach each route have to call next() at the end of your function.
First approach
const express = require('express');
const app = express();
const router = express.Router();
router.post('/', async (req, res) => {
await service.post(req,res);
});
router.get('/', async (req, res) => {
await service.get(req,res)
});
app.use((req,res,next) => {
console.log("always called");
next();
});
app.use('/',router);
Second approach
const express = require('express');
const app = express();
const router = express.Router();
router.post('/', async (req, res, next) => {
await service.post(req,res);
next();
});
router.get('/', async (req, res, next) => {
await service.get(req,res);
next();
});
app.use('/',router);
app.use((req,res) => {
console.log("always called");
});
Thanks for all the answers, they helped me better understand how routing works on Express.
I found another solution, which I think works best in my case - using Router.all() method:
const setRequest = function(req, res, next){
logger.setRequest(request)
next()
}
Router.route('/models/:model_id')
.all(setRequest)
.get(function (req, res) {service.execute()})
.put(function (req, res) {service.execute()})

get express params in an intercepting middleware

I would like to intercept a req.params item in an interception layer:
let's say I have an express server application instance:
const app = express();
with an interception layer:
app.use((req, res, next) => {
console.log(req.params.myParam);
next();
})
and many endpoints like this one:
app.get('/anything/:myParam', (req, res) => res.send('hello'))
this logs 'undefined' which is quite natural because when the interception middleware within the "use" is executed, the param name has not been defined yet. But I really need to known in this interception layer the value of myParam parameter with these constraints:
I known how the parameter is supposed to be named in the intercepted endpoints (myParam)
I can't know how the url is structured in the intercepted endpoints (can be /anything/:myParam, or /:myParam/anything and so on...)
Does anyone know a solution for that case ?
The comment from #indrajaj26 about using app.param() leads me on a working solution.
Here is the way I'm proceeding:
/** express application */
const app = express();
// Interception layer
app.param('myParam', (req, res, next, myParam) => {
/* Store myParam into something reusable (like locals as suggested by #nullromo for instance).
* I'm using zonejs in my case: creating a forked zone with myParam stored in zone properties.
*/
next();
});
// Consuming intercepted data. Anywhere in the callstack of any endpoint using "myParam" as request parameter:
console.log(Zone.current.getZoneWith('myParam')?.get('myParam'));
Thanks everyone for your ideas!
A simple solution would be to pass the middleware function when defining the route.
Example:
function middleware(req, res, next) {
console.log(req.params.myParam);
next();
}
app.get('/path', middleware, function(req, res) {
...
});
Full example:
const express = require('express');
const app = express();
function middleware(req, res, next) {
console.log(req.params.myParam);
next();
}
app.get('/path', middleware, function(req, res) {
...
});
app.listen(3000, () => console.log('server is online'));
You can store the params on the response.locals object for use by later middleware.
const express = require('express');
const app = express();
app.get('/:name', (request, response, next) => {
// generate some kind of result
const result = `Hello, ${request.params.name ?? 'world'}!`;
// save the result and the params onto the request.locals object
response.locals = { params: request.params, result };
// call the next middleware to pass on the params and result
next();
});
app.use((request, response, next) => {
// here you have the params since they were parsed by the previous middleware
console.log(`Intercepted: ${JSON.stringify(response.locals.params)}`);
// now actually send the response that you wanted to send
response.status(200).send(response.locals.result);
});
app.listen(3000, () => {
console.log('listening');
});
This requires storing the request.params on the response.locals object in every handler, which is not the greatest. But it also means that you don't have to specify the interceptor middleware every time, if that's a problem for your case.

Express.js: Pass asynchronous errors thrown

I notice a recurring pattern in my express app which I think could be optimized. Basically I have a route calling a method with some asynchronous functions.
index.js
const controller = require('./controller.js');
const router = new Router();
router.post('/user', controller.createUser);
module.exports = router;
controller.js
exports.createUser = async (req, res, next) => {
try {
// asynchronous calls, etc.
} catch (e) {
// pass error to middleware
next(e);
}
}
The try/catch blocks are recurring in each of my controller methods. I'd want errors caught to be passed to my error-handling middleware. Therefore it seems impractical and repetitive to pass errors in each of my controller functions. Could I refactor this?
What if I wrap the controller method in a function as such:
index.js
const controller = require('./controller.js');
const router = new Router();
const handleErrors = (func) => async (req, res, next) => {
try { await func(req, res, next) }
catch (e) { return next(e) }
};
router.post('/user', handleErrors(controller.createUser));
module.exports = router;
controller.js
exports.createUser = async (req, res, next) => {
// asynchronous calls, etc.
if (a !== b) {
// errors can be passed to middleware as such
throw new CustomError(400, 'a is not equal to b');
}
}
Would this be an appropriate solution? Does Express have any built-in ways of accomplishing the same thing? Should I be cautious about refactoring my entire application in this way?
Would this be an appropriate solution?
Yes, looks nice.
Does Express have any built-in ways of accomplishing the same thing?
No, Express was written before async / await was introduced.
Should I be cautious about refactoring my entire application in this way?
I don't think so. How i would write that:
const handleErrors = (func) => (req, res, next) => func(req, res).then(() => next(), next);
I recommend you this article: https://medium.com/#Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
As in the article, this should be the middleware:
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
This is how a controller should look like:
router.get('/users/:id', asyncMiddleware(async (req, res, next) => {
/*
if there is an error thrown in getUserFromDb, asyncMiddleware
will pass it to next() and express will handle the error;
*/
const user = await getUserFromDb({ id: req.params.id })
res.json(user);
}));
router.post('/users', asyncMiddleware(async (req, res, next) => {
const user = await makeNewUser(req.body);
res.json(user)
}))

Cannot call a class as a function in nodejs express

Hi I am trying to follow ES6 syntax to create a middleware of my node.js application.
index.js
export default class Middleware {
constructor() {
//do nothing
}
fun1 = (req, res, next) => {
console.log("------------------------------------");
console.log("AAa");
console.log("------------------------------------");
next();
};
fun2 = (req, res, next) => {
console.log("------------------------------------");
console.log("AAa");
console.log("------------------------------------");
next();
};
}
app.js
import Middleware from ".index";
app.use(Middleware);
I am getting an error Cannot call a class as a function. Does anyone know what is wrong?
Express app#use expects a function with the following signature:
function(req, res, next) {
To make it work, you need to do:
Create an instance of Middleware class.
Register middleware for each function in the class.
Example:
let middleware = new Middleware();
app.use(middleware.func1);
app.use(middleware.func2);

javascript adding a value to function arguments

I'm trying to add an argument to a function passed along as an argument in an express js route.
This is an expressjs route:
app.get('path', someFunc,
function(req, res, next) {
res.render('layout');
});
The function someFunc right there takes the arguments req, res, next.
I want to add an additional argument to it. If I use apply or call it seems to replace all the existing arguments.
I want to be able to do:
someFunction (req, res, next, custom) {}
How can I do this? Thanks.
I am not sure that this is the best way but you could do something like this :
var someFunc = function (req, res, next, custom) { console.log(custom); next(); }
app.get('path',
function (req, res, next) { someFunc(req, res, next, 'custom') },
function(req, res, next) {
res.render('layout');
});
I would create a route like this:
// Inside your routes.js:
module.exports.someRoute = function(myArgument) {
return function(req, res, next) {
// Do whatever you want with myArgument.
};
};
// Inside your app.js:
app.get('path', routes.someRoute({ foo: 1 }));
This way your route setup is clear of any logic.

Categories