express.js 4: catch bodyParser error in router's errorHandler

2k views Asked by At

How can I catch a bodyParser error in a router's errorHandler in express.js 4?

Example code:

var express = require('express');
var bodyParser = require('body-parser');

var app = express();

// use bodyParser.json()
app.use(bodyParser.json());

// setup route /test
var testRouter = express.Router();
testRouter.post('/', function(req, res, next) {
  next(new Error('not implemented'));
});
testRouter.use('/', function(err, req, res, next) {
  res.send('testRouter error: ' + err.message);
});
app.use('/test', testRouter);

// default error handler
app.use(function(err, req, res, next) {
  res.send('default error handler: ' + err.message);
});

module.exports = app;

Sending a POST request to /test with an empty body will return testRouter error: not implemented. This message comes from the error handler which is defined in the testRouter, and this is what I expect to happen.

Sending a POST request to /test with a malformed json body, however, will return default error handler: [bodyParser error]. This message comes from the default error handler.

I want the error handler in the testRouter to handle all bodyParser errors that occur on requests to /test. How can I achieve that?

2

There are 2 answers

3
robertklep On BEST ANSWER

You need to have testRouter use the body parser middleware:

// not this:
app.use(bodyParser.json());

// but this:
testRouter.use(bodyParser.json());

Otherwise the default router will handle the error, like you noticed.

If you also want to use the body parser middleware for the default router, make sure that you declare it after you added testRouter:

app.use('/test', testRouter);
app.use(bodyParser.json());
0
x-ray On

robertklep's answer is the answer to my question how to catch bodyParser errors in a router's errorHandler. However, I found a slightly different solution without catching the error inside the router. My basic idea was that I wanted to put everything related to an api exposed under /api/v1.0 into a router in a javascript module - including an error handler that outputs errors in the format required by the api. Thus I had the following line in app.js:

app.use('/api/v1.0', require('./routes/api/v1.0/api'));

api.js exported an express.Router(). This router would never handle errors from 'outside' of the router. (See also GitHub issues catch error of "toplevel" app.use-middlewear in router error handler #2679 and Inconsistent behavior with error-handling middleware in routers #2633).

To catch all errors with an api specific error handler, api router and api error handler have to be separated. That can be done like this:

api.js:

var express = require('express');
var router = express.Router();

router.use('/endpoint', require('./endpoint'));

var errorHandler = function (err, req, res, next) {
  // all errors need to be returned in json
  res.status(err.status || 500);
  res.json({ error: err.message });
};

module.exports.router = router;
module.exports.errorHandler = errorHandler;

app.js:

var api = require('./routes/api/v1.0/api');
app.use('/api/v1.0', api.router);
app.use('/api/v1.0', api.errorHandler);

This way the api's error handler still only handles requests to /api/v1.0, but can catch all errors, including e.g. errors from bodyParser defined as app-level middleware.