Express middleware error handler - node.js

Following a generated template for express. In the app.js there is a following snippet
app.use('/users', users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
Per my understanding, the middleware will run in order from app.use('/users', users) to 404 handler to error handler. My question is how will it be possible to reach error handler and execute this line res.status(err.status || 500); ? Wont every failed request be passed through 404 handler first and therefor getting the status code of 404? Please let me know if I am missing something! Thank you

No, it won't. If you look at these event handlers declarations you'll see that error handler for unhandled error, has an additional err parameter:
app.use(function(req, res, next) {
app.use(function(err, req, res, next) {
Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors. For details about error-handling middleware.
So, when the route is not found, the last declared middleware is calling, it's 404 error handler.
But when you call next with error: next(err) or your code throws an error, the last error handler is calling.

System error should be handled before 404
app.use('/users', users);
// error handler
app.use(function(err, req, res, next) {
// No routes handled the request and no system error, that means 404 issue.
// Forward to next middleware to handle it.
if (!err) return next();
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
// catch 404. 404 should be consider as a default behavior, not a system error.
app.use(function(req, res, next) {
res.status(404);
res.render('Not Found');
});

Wont every failed request be passed through 404 handler first and therefor getting the status code of 404?
No, the 404 route is just a standard middleware and usually wired up last, meaning if no other routes handle the request they will eventually hit the 404.
The 500 is a special type of middleware, you'll notice it has 4 parameters (first one being an error param). This middleware is invoked as soon as you call next with an error, or any sort of data for that matter.
See the docs

Something to note for anyone else that stumbles around this post.
Your last middleware to be invoked (if none gets invoked) - will be 404, it doesn't take an err as its middleware parameter. Hence, you can't/shouldn't try to catch the 404 before any other middleware. So your last middleware should be without the err parameter like the example below and of course, should be at the bottom of your application stack.
//at the botton of your app.js
// catch 404.
//no err parameter
app.use(function(req, res, next) {
res.render("404Page"{
url: req.url
});
});
If you want to catch your errors before it goes to this handler use a (err, req, res, next) before this middleware.

Related

Prevent stack trace leak in production in Nodejs application

I was looking at this Nodejs express Guthub repo. The production strack trace leak prevention is done using:
// development error handler
// will print stacktrace
if (!isProduction) {
app.use(function(err, req, res, next) {
console.log(err.stack);
res.status(err.status || 500);
res.json({'errors': {
message: err.message,
error: err
}});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.json({'errors': {
message: err.message,
error: {}
}});
});
I do not understand why the production strack trace middleware does not have to be inside the else condition
In the example you shared, an else block would probably make things a bit more clear, but it's not strictly needed. If isProduction is truthy, the error handling middleware inside the if block will handle the error. Since it does not call next(err); to pass the request to the next middleware, control will not be passed to the following "production" error-handling middleware. The response is sent and Express is done handling this particular request.
If isProduction is falsey, obviously the "development" middleware is not ever registered with Express and all errors will be handled in the "production" error-handler.
The above example, however, would break down if you needed to add another error-handling middleware after the "production" error-handler or something.
More Details
In Express, middlewares are run sequentially in the order in which they were registered and control is passed to the next middleware by using the calling the next parameter. See the documentation here.
Error handling middleware, like the ones posted in your example, work the same way. The error will reach these middlewares because a previous middleware called next and passed an error like so: next(new Error('Something went wrong));.

Why might Express error handling middleware not receive req.session?

Environment: node.js, Express, express-session package.
Background: I was testing an Express error handler and I got an error that I wasn't expecting.
The following simplified route throws an error.
TypeError: Cannot read property 'userValues' of undefined
exports.errorHandler = wrapAsync(async function(error, req, res, next) {
let loggedIn = req.session.userValues ? true : false;
res.render('error', { loggedIn });
});
However when I remove the error parameter it works without an error as I had anticipated.
exports.errorHandler = wrapAsync(async function(req, res, next) {
let loggedIn = req.session.userValues ? true : false;
res.render('error', { loggedIn });
});
Why might this be?
The basic pattern in the second example works in several dozen routes that don't include the error parameter.
You could use something like this. And it will only get executed whenever there is an ERROR 500 unless you passed the ERROR 404 to this one using next() function, if you handled all the errors correctly you should be able to make an ERROR 500 and this should be able to catch that ERROR.
const errorHandler = require("./your-file");
...
... every other route even error 404 handler
...
app.use(errorHandler);
What do I mean by using next() for ERROR 404
If you have used express-generator then you should already have this piece of code:
// catch 404 and forward to error handle
app.use((req, res, next) => {
next('not found');
});
The end file should looks something like this now if you use this approach:
...
... all previous routes
...
// catch 404 and forward to error handle
app.use((req, res, next) => {
next('not found');
});
// handle all other error
app.use(errorHandler);
Hope this helps

Why is express error handling 404 on all routes, even ones that work?

So I've set up a blank brand new express app using the express generator.
I added a console.log to the error handler:
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
console.log(err); //<-- This is the added line
// render the error page
res.status(err.status || 500);
res.render('error');
});
And then when hitting the index of my app I get this in the console:
GET / 200 9.521 ms - 170
GET /stylesheets/style.css 200 2.099 ms - 111
{ [Error: Not Found] status: 404 }
Surely this code shouldn't even be called when a successful route is it? Is this meant to happen?
My guess is that the request that's causing the 404 is for /favicon.ico. The other requests have been handled successfully (hence the 200 status that gets logged, right after the request path).

How to trigger router error handler instead of default error handler in expressjs

My express app uses the default JSON body parser:
app.use(bodyParser.json());
Further down the app.js file I have my own router for building REST API routes:
var api = require('./routes/api/index');
...
app.use('/api', api);
This router has an error handler, among other things:
router.use(function (err, req, res, next) {
debugger;
res.status(err.code ? getParseErrorStatus(err.code) : res.status || 500).send({
error: err.message
});
});
Whenever the bodyParser throws an error while parsing the request body, I get my generic express error handler called:
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function (err, req, res, next) {
debugger;
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
This holds true even for calls to /api/* URLs that are caught by the API router.
Instead of always calling the generic error handler, how can I make JSON parsing errors (caught by middleware up in the chain) call the error handler I've defined in the API router, if the URL is an API URL?
Error handlers are called at the same router stack level. Since the bodyParser.json() is executed at the main/root/app layer, it will look for the first error handler at the main/root/app layer.
So if you want to handle API body parsing errors, you will have to move your bodyParser.json() middleware to each of your routes/routers that require body parsing.

Use process.on('uncaughtException to show a 500 error page

I'm developing an express app.
I currently have the following in my server.js
process.on('uncaughtException', function (err) {
console.log( "UNCAUGHT EXCEPTION " );
console.log( "[Inside 'uncaughtException' event] " + err.stack || err.message );
});
Which stops the server crashing every time there's an error, however, it just sits there...
is it possible to instead of console.log, to send a 500 error page with the error details?
I don't think you can from within the uncaughtException do a response since that could happen even when there is no request occurring.
Express itself provides a way to handle errors within routes, like so:
app.error(function(err, req, res, next){
//check error information and respond accordingly
});
Per ExpressJS Error Handling, add app.use(function(err, req, res, next){ // your logic }); below your other app.use statements.
Example:
app.use(function(err, req, res, next){
console.log(err.stack);
// additional logic, like emailing OPS staff w/ stack trace
});

Resources