I am developing express app and after I specify all my routes and middlewares I have this at the end of server.js:
// Log errors
app.use(function (err, req, res, next) {
logger.error(err.stack);
if(process.env.NODE_ENV === 'production')
return res.status(500).send('Something broke!');
next(err);
});
// Start server
app.listen(port, () => {
logger.info('Server is up on port ' + port);
});
The purpose of this is to catch ALL the errors on production and to avoid leaking sensitive data to the client.
I have this code in one of my controllers:
const createHTTPError = require('http-errors')
async function(req, res, next) {
try {
invoice = await Invoice.create({
// data
});
}catch (e) {
if(e instanceof Sequelize.ValidationError){
logger.error(e);
return next(createHTTPError(400, 'Validation did not pass: ' + e.message));
}
}
}
The problem is, that when next()
is called with http-errors
object, it bubbles up to my catch-all error handler but information is lost and inside it the err object is a simple Error
instance with these params:
message = "Validation did not pass: notNull Violation: invoice.clientEmail cannot be null"
name = "BadRequestError"
stack = "BadRequestError: Validation did not pass: notNull Violation: invoice.clientEmail cannot be null\n at module.exports (/home/XXXX/create-new-invoice.js:109:33)"
Error code number is lost. Error object type is lost (well, converted to string in name).
What should I do? If I remove my catch-all, I am risking that some sensitive info will be leaked. Thanks
When the error occurs in the HTTP Request it is intercepted and invokes the catchError . Inside the catchError you can handle the error and then use throwError to throw it to the service. We then register the Interceptor in the Providers array of the root module using the injection token HTTP_INTERCEPTORS .
For error handling, we have the next(err) function. A call to this function skips all middleware and matches us to the next error handler for that route. Let us understand this through an example. var express = require('express'); var app = express(); app.
To handle an error in an asynchronous function, you need to catch the error first. You can do this with try/catch . Next, you pass the error into an Express error handler with the next argument. If you did not write a custom error handler yet, Express will handle the error for you with its default error handler.
So I ended up with this code:
const HTTPErrors = require('http-errors');
const HTTPStatuses = require('statuses');
// ... set up express, middlewares, routes...
// Log errors
app.use(function (err, req, res, next) {
let messageToSend;
if(err instanceof HTTPErrors.HttpError){
// handle http err
messageToSend = {message: err.message};
if(process.env.NODE_ENV === 'development')
messageToSend.stack = err.stack;
messageToSend.status = err.statusCode;
}else{
// log other than HTTP errors (these are created by me manually, so I can log them when thrown)
logger.error(err.stack);
}
if(process.env.NODE_ENV === 'production' && !messageToSend){
messageToSend = {message: 'Something broke', status: 500};
}
if(messageToSend) {
let statusCode = parseInt(messageToSend.status,10);
let statusName = HTTPStatuses[statusCode];
res.status(statusCode);
// respond with html page
if (req.accepts('html')) {
res.send('<html><head><title>'+statusCode+' '+statusName+'</title></head><body><h1>'+statusCode+' '+statusName+'</h1>'+messageToSend.message+'<br/><br/>'+(messageToSend.stack ? messageToSend.stack : '')+'</body></html>');
return;
}
// respond with json
if (req.accepts('json')) {
let responseObject = { error: statusName, code: statusCode, message: messageToSend.message };
if(messageToSend.stack)
responseObject.stack = messageToSend.stack;
res.send(responseObject);
return;
}
// default to plain-text. send()
res.type('txt').send(statusName+' '+messageToSend.message);
return;
}
// if this is not HTTP error and we are not in production, let express handle it the default way
next(err);
});
This solution:
http-errors
module (with stack trace for development and without for production)I also take advantage of this new catchall function in 404 catchall:
// DEFAULT CATCH
app.use(function(req, res, next){
next(HTTPErrors(404));
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With