Hey guys I want to achieve central error handling in express, I've done this.
app.use(function(err,req,res,next){
logger.error(err);
utils.jsonOutHandler(err,null,res);
next(err);
});
app.get('/',(req,res)=>{
throw new Error('Testing');
});
Also I made a special jsonOutHandler method which sends proper response to the user.
function jsonOutHandler(err, result, out) {
if (err) {
let status = 500;
const message = 'Something broke';
if (err instanceof DbError) {
status = 500;
}
if (err instanceof ValidationError) {
status = 400;
}
if (err instanceof SystemError) {
status = 500;
}
out.status(status).send({message});
return;
}
out.status(result.status).send({data: result.data});
}
But whenever I throw error on '/' route my error handler is never triggered. Why?
Express is based on middlewares, so, if you wanna catch the errors inside the middleware, you should call the error middleware:
app.get('/',(req,res)=>{
next(new Error('Testing'));
});
/**
* middleware to catch errors happened before this middleware
* express knows this middleware is for error handling because it has
* four parameters (err, req, res, next)
**/
app.use((err, req, res, next) => {
res.status(500).send({
message: err.message,
});
});
I hope you can adapt this example to your requirements. The thing to keep in mind is an error middleware can be used from previous middlewares. In your example you couldn't catch the error because your middleware was defined before your main router app.get('/')
Related
I have a problem and I haven't found a solution yet. I want to catch all errors if occur any error in each route but it's very inconvenient when I have to do it many times.
How can i implement it as a middleware same like app.use(ErrorHandle); ?
Code in ErrorHandler.js:
export const ErrorHandler = func => async (req, res, next) => {
try {
await func(req, res, next);
} catch (error) {
next(error);
}
}
Code in index.js
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
Code in route need to catch error:
import { ErrorHandler } from './ErrorHandler';
export const uploadMedia = ErrorHandler(async (req, res) => {
// do something...
let error = new Error();
error.statusCode = 404;
error.message = 'Content not found!';
}
Sorry if misunderstood your question... When you do the code below which you provided, you are assuming that if an error reaches the end of the stack via next(err), such handler should be called. Hence it's the last declaration after all your routes.
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
That, however, won't catch unhandledExceptionErrors. You still need the good old
try {
// throw error somewhere
} catch (e) {
next(e);
}
Personally, I haven't tried this package but it seems so be a nice hack for Express router's inability to handle promise returns. About express-promise-router:
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.
I a trying to implement a rest API for our project then I go for node js and express. I have built all the models and controllers. I faced an issue while trying to handle an error. Errorhandler function doesn't receive all the properties of error that caught in try/catch block. I can not read its name in a handler but I can use its name in the controller. Could you please help me?
const errorHandler = (err, req, res, next) => {
console.log(`Error in method:${req.method}: ${err.stack}`.bgRed);
let error = { ...err };
console.log(`Error handler: ${err.name}`);
res.status(error.statusCode || 500).json({
success: false,
data: error.message || 'Server Error',
});
};
module.exports = errorHandler;
controller
const mongoose = require('mongoose');
const Product = require('../models/Product');
const ErrorResponse = require('../utils/error');
const routeName = 'PRODUCT';
// #desc getting single product via id
// #route GET api/v1/products
// #acces public
exports.getProdcut = async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return next(
new ErrorResponse(`Product not found with id:${req.params.id}`, 404)
);
}
res.status(200).json({
success: true,
data: product,
});
} catch (err) {
console.log(err.name);
console.log('ERRO APPEND');
next(new ErrorResponse(`Product not found with id:${req.params.id}`, 404));
}
};
Assuming that errorHandler is part of your middleware that is somewhere after getProdcut, you can try just throwing the error and Express will automatically detect that for you, because error handling middleware such as yours accepts 4 parameters. So the following would work:
const getProdcut = async (req, res, next) => {
try {
// ...
} catch (err) {
throw err;
}
};
const errorHandler = (err, req, res, next) => {
if (err) {
console.log('hello from the error middleware');
console.log(err.name);
}
else {
// next() or some other logic here
}
}
app.use('/yourRoute', getProdcut, errorHandler);
And inside of your errorHandler you should have access to the error object.
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.
https://expressjs.com/en/guide/using-middleware.html#middleware.error-handling
Can someone expound on the times when it's appropriate in a node.js Express app to throw an error like so:
throw new Error('my error');
or to pass this error on via the callback usually labelled 'next' like so:
next(error);
and could you please explain what each of them will do in the context of an Express app?
for example, here is an express function dealing with URL parameters:
app.param('lineup_id', function (req, res, next, lineup_id) {
// typically we might sanity check that user_id is of the right format
if (lineup_id == null) {
console.log('null lineup_id');
req.lineup = null;
return next(new Error("lineup_id is null"));
}
var user_id = app.getMainUser()._id;
var Lineup = app.mongooseModels.LineupModel.getNewLineup(app.system_db(), user_id);
Lineup.findById(lineup_id, function (err, lineup) {
if (err) {
return next(err);
}
if (!lineup) {
console.log('no lineup matched');
return next(new Error("no lineup matched"));
}
req.lineup = lineup;
return next();
});
});
In the line commented "//should I create my own error here?"
I could used "throw new Error('xyz')", but what exactly would that do? Why is it usually better to pass the error to the callback 'next'?
Another question is - how do I get "throw new Error('xyz')" to show up in the console as well as the browser when I am in development?
In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next', also an error handler needs to be defined so that all the errors passed to 'next' can be handled properly.
http://expressjs.com/en/guide/error-handling.html
Throwing an error inside a callback doesn't work:
app.get('/', function (req, res) {
fs.mkdir('.', (err) => {
if (err) throw err;
});
});
But calling next works:
app.get('/', function (req, res, next) {
fs.mkdir('.', (err) => {
if (err) next(err);
});
});
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:
app.get('/', function (req, res) {
throw new Error('BROKEN') // Express will catch this on its own.
})
For those who prefer throwing errors, here is a workaround decorator:
export function safeThrow(
target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<(req: Request, res: Response, next: NextFunction) => Promise<any>>) {
const fun = descriptor.value;
descriptor.value = async function () {
try {
await fun.apply(this, arguments);
} catch (err) {
arguments[2](err);
}
};
}
#safeThrow
private async get(req: Request, res: Response, next: NextFunction) {
throw { status: 404, message: 'Not supported' }
}
I'm developing a Node.js/CoffeeScript app where I'm using a class hierarchy for errors. Everything works great when I'm using throw statements in the root of a route handler:
class APIError extends Error
constructor: ->
app.error (err, req, res, next) ->
if err instance of APIError
console.log 'APIError captured'
app.get '/', (req, res, next) ->
throw new APIError
However, with Mongoose, using throw statement in a callback function:
app.get '/', (req, res, next) ->
UserModel.where('name', 'Joe').findOne (err, doc) ->
throw new APIError
results in
/usr/local/lib/node_modules/mongoose/lib/utils.js:413
throw err;
^
Error:
When I call next() instead, as in
app.get '/', (req, res, next) ->
UserModel.where('name', 'Joe').findOne (err, doc) ->
return next new APIError
or even use it in the main body of a handler:
app.get '/', (req, res, next) ->
next new APIError
I get undefined being printed out in the console.
Changing the last statement to return next Error works as expected, i.e. an exception stack trace is being printed out in the console:
app.get '/', (req, res, next) ->
return next Error 'This works as expected'
Is it Node.js issue or the way I'm defining classes in CoffeeScript? Any ideas how to make such error hierarchy to work?
Update 1
I can confirm that it's the way CoffeeScript's classes are implemented. Using standard JS prototype chain definitions solves the problem, but it does not feel right.
Setting name attribute of the class in its constructor solves the problem (line no. 3):
class APIError extends Error
constructor: ->
#name = 'APIError'
app.use (err, req, res, next) ->
if err instance of APIError
console.log 'Error captured'
app.get '/', (req, res, next) ->
#This will work: captured by error handler
throw new APIError
app.get '/in-mongoose-callback', (req, res, next) ->
UserModel.where('name', 'Joe').findOne (err, doc) ->
#This one works as expected: captured by error handler
return next new APIError
This is due to one of the changes that landed in CoffeeScript 1.3.3 (MAY 15, 2012):
Due to the new semantics of JavaScript's strict mode, CoffeeScript no longer guarantees that constructor functions have names in all runtimes. See #2052 for discussion.
Note that using throw statement instead of next() inside a Mongoose query callback will not work.
While using express, I extend Error, set the name as prototype property, and perform a stack trace.
class RequestError extends Error
name: 'RequestError'
constructor:(#status, message)->
super message
Error.captureStackTrace #, #constructor
I can do new RequestError 403, 'Invalid username/password.' for a bad user signin, or new RequestError 404, 'Page not found.' for a bad page request. When I catch the error I do
require 'colors'
console.error error.stack.red # logs where any error occured
if error instanceof RequestError
# proper response for user errors
response.status(error.status).send(error.message)
else response.status(500).send('Internal Server Error') # for 'other' errors the user only gets back a 500 'Internal Server Error'
It's hard to tell but we had issues on errors until we moved them to the end of the server script, even after the start server. This is using Express with Node, but could give you a hint. Before we had this near the beginning of server file assuming no issues but started working after we moved all our error handling to end. Unsure if router or middleware of framework order of operations issue but seemed to fix our issues.
/////////////////////////////
// Start Server
/////////////////////////////
app.listen(siteConf.port);
//////////////////////////
// Error Handling
//////////////////////////
function NotFoundError(req, message){
this.name = "NotFoundError";
this.status = 404;
this.message = message || (req.method + " " + req.url + " could not be found");
Error.captureStackTrace(this, NotFoundError);
}
NotFoundError.prototype.__proto__ = Error.prototype;
function ProtectedResourceError(req, message){
this.name = "ProtectedResourceError";
this.status = 401;
this.message = message || (req.url + " is protected");
}
ProtectedResourceError.prototype.__proto__ = Error.prototype;
function ValidationError(req, message, errors){
this.name = "ValidationError";
this.status = 400;
this.message = message || ("Error when validating input for " + req.url);
this.errors = errors;
}
ValidationError.prototype.__proto__ = Error.prototype;
function SearchError(req, message){
this.name = "SearchError";
this.status = 400;
this.message = message || ("Error when searching");
}
SearchError.prototype.__proto__ = Error.prototype;
// If no route matches, throw NotFoundError
app.use(function(req, res, next) {
console.log("No matching route for " + req.url);
next(new NotFoundError(req));
});
// General error handler
app.use(function(err, req, res, next) {
//console.log("Trapped error : " + err);
// handle exceptions
if (err instanceof ValidationError) {
standardResponse(req, res, {error: err.message, fields: err.errors}, err.status, err.message);
} else if (err instanceof ProtectedResourceError) {
standardResponse(req, res, {error: err.message}, err.status, err.message);
} else if (err instanceof NotFoundError) {
standardResponse(req, res, {error: err.message}, err.status, err.message);
} else if (err instanceof SearchError) {
standardResponse(req, res, {error: err.message}, err.status, err.message);
} else {
standardResponse(req, res, {error: err.message}, 500, err.message);
}
});
I have tried:
app.get('/', function(req, res, next) {
var e = new Error('error message');
e.status = 400;
next(e);
});
and:
app.get('/', function(req, res, next) {
res.statusCode = 400;
var e = new Error('error message');
next(e);
});
but always an error code of 500 is announced.
Per the Express (Version 4+) docs, you can use:
res.status(400);
res.send('None shall pass');
http://expressjs.com/4x/api.html#res.status
<=3.8
res.statusCode = 401;
res.send('None shall pass');
A simple one liner;
res.status(404).send("Oh uh, something went wrong");
I'd like to centralize the creation of the error response in this way:
app.get('/test', function(req, res){
throw {status: 500, message: 'detailed message'};
});
app.use(function (err, req, res, next) {
res.status(err.status || 500).json({status: err.status, message: err.message})
});
So I have always the same error output format.
PS: of course you could create an object to extend the standard error like this:
const AppError = require('./lib/app-error');
app.get('/test', function(req, res){
throw new AppError('Detail Message', 500)
});
'use strict';
module.exports = function AppError(message, httpStatus) {
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
this.status = httpStatus;
};
require('util').inherits(module.exports, Error);
You can use res.send('OMG :(', 404); just res.send(404);
In express 4.0 they got it right :)
res.sendStatus(statusCode)
// Sets the response HTTP status code to statusCode and send its string representation as the response body.
res.sendStatus(200); // equivalent to res.status(200).send('OK')
res.sendStatus(403); // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404); // equivalent to res.status(404).send('Not Found')
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
//If an unsupported status code is specified, the HTTP status is still set to statusCode and the string version of the code is sent as the response body.
res.sendStatus(2000); // equivalent to res.status(2000).send('2000')
From what I saw in Express 4.0 this works for me. This is example of authentication required middleware.
function apiDemandLoggedIn(req, res, next) {
// if user is authenticated in the session, carry on
console.log('isAuth', req.isAuthenticated(), req.user);
if (req.isAuthenticated())
return next();
// If not return 401 response which means unauthroized.
var err = new Error();
err.status = 401;
next(err);
}
The version of the errorHandler middleware bundled with some (perhaps older?) versions of express seems to have the status code hardcoded. The version documented here: http://www.senchalabs.org/connect/errorHandler.html on the other hand lets you do what you are trying to do. So, perhaps trying upgrading to the latest version of express/connect.
I tried
res.status(400);
res.send('message');
..but it was giving me error:
(node:208) UnhandledPromiseRejectionWarning: Error: Can't set headers
after they are sent.
This work for me
res.status(400).send(yourMessage);
Old question, but still coming up on Google. In the current version of Express (3.4.0), you can alter res.statusCode before calling next(err):
res.statusCode = 404;
next(new Error('File not found'));
Express deprecated res.send(body, status).
Use res.status(status).send(body) or res.sendStatus(status) instead
I would recommend handling the sending of http error codes by using the Boom package.
Async way:
myNodeJs.processAsync(pays)
.then((result) => {
myLog.logger.info('API 200 OK');
res.statusCode = 200;
res.json(result);
myLog.logger.response(result);
})
.fail((error) => {
if (error instanceof myTypes.types.MyError) {
log.logger.info(`My Custom Error:${error.toString()}`);
res.statusCode = 400;
res.json(error);
} else {
log.logger.error(error);
res.statusCode = 500;
// it seems standard errors do not go properly into json by themselves
res.json({
name: error.name,
message: error.message
});
}
log.logger.response(error);
})
.done();
If you want to send the status code without its string representation you can do:
res.status(200).send();