Search code examples
node.jsexpresserror-handlingmongoosepromise

Error handling in Node.js + Express using promises


Using Node.js + Express (4) + Mongoose (using promises rather than callbacks), I can’t sort out how to tidy up my error handling.

What I've got (rather simplified) is:

app.get('/xxx/:id', function(request, response) {
    Xxx.findById(request.params.id).exec()
        .then(function(xxx) {
            if (xxx == null) throw Error('Xxx '+request.params.id+' not found');
            response.send('Found xxx '+request.params.id);
        })
        .then(null, function(error) { // promise rejected
            switch (error.name) {
                case 'Error':
                    response.status(404).send(error.message); // xxx not found
                    break;
                case 'CastError':
                    response.status(404).send('Invalid id '+request.params.id);
                    break;
                default:
                    response.status(500).send(error.message);
                    break;
            }
        });
});

Here, in the switch in the ‘promise rejected’ section, the Error is the error I threw myself for a potentially valid id which is not found, the CastError is Cast to ObjectId failed thrown by Mongoose for an invalid id, and the 500 error can for instance be triggered by mistyping throw Error() as throw Err() (causing a ReferenceError: Err is not defined).

But like this, every one of my routes has this great big clumsy switch to handle the different errors.

How can I centralise the error handling? Can the switch be tucked away into some middleware somehow?

(I did hope I could just re-throw using throw error; within the 'promise rejected' block, but I haven’t been able to make it work).


Solution

  • I would create middleware to handle errors. Using next() for 404s. and next(err) for other errors.

    app.get('/xxx/:id', function(req, res, next) {
      Xxx.findById(req.params.id).exec()
        .then(function(xxx) {
          if (xxx == null) return next(); // Not found
          return res.send('Found xxx '+request.params.id);
        })
        .then(null, function(err) {
          return next(err);
        });
    });
    

    404 handler

    app.use(function(req, res) {
      return res.send('404');
    });
    

    Error handler

    app.use(function(err, req, res) {
      switch (err.name) {
        case 'CastError':
          res.status(400); // Bad Request
          return res.send('400');
        default:
          res.status(500); // Internal server error
          return res.send('500');
      }
    });
    

    You can improve upon this more by sending a json response like:

    return res.json({
      status: 'OK',
      result: someResult
    });
    

    or

    return res.json({
      status: 'error',
      message: err
    });