Return inside express router middleware - javascript

If I have a simple express router that just returns nothing (undefined), is there a way to recover from it or will the request just hang until there's a timeout?
For example if I had this code:
const express = require('express');
const app = express();
const router = express.Router();
router.use((req, res, next) => {
console.log('stuck');
return;
});
app.use(router);
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(3000, () => console.log('Example app listening on port 3000!'));
The app hangs after printing stuck. If I return next() the request continues, but I'm trying to see if there's a way to recover from an empty return statement without calling next() at some point.
Thanks in advance!

Since you don't want to call next(), you could probably wrap the handler.
Something like this:
const withReturnNext = (func) => (req, res, next) => {
func(req, res);
next();
}
app.use(withReturnNext((req, res) => res.set('x-headername', 'value'));
However, this is not advisable because any async code will return before the asyncronous action is finished, leading to next() being called before it was supposed to.

You have to use next() to continue the middleware chain. If you return from the function you just terminate the chain and if subsequent function is responsible for the response you won't get it. If you want to terminate (for example because something went wrong) but still finish the request you can use something like that:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something went wrong')
})

Related

I am getting error that "next is not a function" . The same code is working for the other source which i am preferring from one sir

const express=require('express');
const app=express()
//middleware
const middleware=(req,res,next)=>{
console.log('this is just a middleware in console');
next();
}
middleware()
app.get('/about',middleware,(req,res)=>{
res.send('hi this is about');
console.log('about in console');
})
app.listen(3000,()=>{
console.log('hi');
})
please tell the reason so that i can solve it , please as soon as possible, help me.
What you want to do is just define another middleware targeting your '/about', instead of creating a function called middleware and calling it all by itself.
A solution you could use is this:
const express=require('express');
const app=express()
app.get('/about', (req, res, next) => {
console.log('this is just a middleware in console');
next();
});
app.get('/about', (req,res, next)=>{
res.send('hi this is about');
console.log('about in console');
})
app.listen(3000,()=>{
console.log('hi');
})
This way you're handling middleware logic in the middleware function above your final middleware function where you ultimately send a response back to the client.

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.

Why after sending a response, node is still executing further middleware in Express?

so i have a very basic blog app made in express, and the thing is that when i respond to app.get('/') and render a home page view, the server is still responding to app.get('/:id') that is further in the code.
I didn't call next() in app.get('/'), and from what i know, when i render a view, then the rest of middleware after shouldn't get executed, so why is it executing app.get('/:id')?
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// Setting up basic middleware
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
app.set('views');
// Database things
// ...
// Home page
app.get('/', (req, res) => {
res.render('index')
})
// Get add new blog page
app.get('/add-new', (req, res) => {
res.render('addblog')
})
// Respond with single blog page
app.get('/:id', (req, res) => {
const id = req.params.id;
Blog.findById(id)
.then(data => res.render('blog', { data }))
})
// 404
app.use('/', (req, res) => {
res.render('404')
})
I did not understand properly what you meant to ask but from what I understood, I think you mean to ask that even if someone calls /123, (Here 123 is the id) the code to be executed should be
app.get('/', (req, res) => {
res.render('index')
})
If this is what you mean to ask, then that's not how it's done. Only www.yoursite.com/ request goes to app.get('/', (req, res) and any route having value after www.yoursite.com/ like www.yoursite.com/123 will go to the other route which renders single page. 1 more thing. As you have written that if you didn't call next, rest of the middleware shouldn't execute after render, that is not the case. The entire middleware has to execute unless there is an error or you call the return statement like return res.render or simply return.
your 404 Route should be :
app.get('*', function(req, res){
res.render('404');
});
every middelware you have to return something in you case:
return res.render('the-page')
without return it will not work

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)
}))

Categories