Basti
Basti

Reputation: 731

How to use express.Router instance for error handling

According to the documentation, any nodejs express middleware function can be replaced by App or Router instances:

Since router and app implement the middleware interface, you can use them as you would any other middleware function.

This is some generic error handling I use:

express()
    .use('/test', new TestRouter())
    .use((err, req, res, next) => {
        console.error(err.stack);
        res.status(500).send(err.message);
    })
    .listen(PORT);

I tried to replace my error handling with an error-handling Router, but now the callback is never executed and express uses it's default error handling.

const handler = new express.Router();
handler.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send(err.message);
});

express()
    .use('/test', new TestRouter())
    .use(handler)
    .listen(PORT);

Why is this not working as expected and how could I solve it?

Upvotes: 5

Views: 2920

Answers (1)

James
James

Reputation: 82096

Error handlers need to be configured as the last calls to use, as per the docs

You define error-handling middleware last, after other app.use() and routes calls;

I would say "routes" also buckets Routers therefore what you are trying to do doesn't look like it's supported.


Just digging further into this, I believe the issue is down to the fact Routers have separate Layer stacks. Express behind the scenes is effectively just a Router (we see here where this is setup, and then further down where the middleware is delegated on to the Router). Internally, middleware functions are represented as "Layers" (as seen here) and looking at the constructor, we can see Routers have their own stack.

So consider the following example:

express()
  .use(new RouterA())
  .use(new RouterB())
  .use((err, req, res, next) => {
    ...
  })
  .listen(PORT);

Which, by looking at the source, can be viewed as:

express()
  .use((req, res, next) => {
    router.handle(req, res, next);
  })
  .use((req, res, next) => {
    router.handle(req, res, next);
  })
  .use((err, req, res, next) => {
    ...
  });

Therefore, if an error is thrown in RouterA, the Router would firstly check its own middleware stack for a matching error Layer (i.e. a (err, req, res, next) function) and execute that, it would then bubble up to the app level and perform the same action.

So given your example, if you consider the translated code, this explains why it won't catch your error handler in the second Router - a Router signature does not match that of an error handler therefore it would be skipped.

Upvotes: 4

Related Questions