Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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).

like image 556
ChrisV Avatar asked Mar 19 '23 03:03

ChrisV


1 Answers

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
});
like image 76
Jordonias Avatar answered Apr 02 '23 09:04

Jordonias