Errors in Express

Check out http-errors package

Sync

Sync errors will just go to error handler(see below)

Async

Crash the server:

app.get("/", async (req, res) => {
  throw new Error("non async");
}

Send to Error Handler:

app.get("/", async (req, res, next) => {
  next(new Error("non async"));
}

Passing anything to next(expect the string 'route') will pass to 4 arg error middleware

Async Solutions

Async Wrapper

app.get('*', wrapAsync(async (req, res) => {
  await new Promise(resolve => setTimeout(() => resolve(), 50));
  // Async error!
  throw new Error('woops');
}));

app.use(function(error, req, res, next) {
  // Gets called because of `wrapAsync()`
  res.json({ message: error.message });
});

function wrapAsync(fn) {
  return function(req, res, next) {
    // Make sure to .catch() any errors and pass them along to the `next()` middleware in the chain, in this case the error handler.
    fn(req, res, next).catch(next);
  };
}

Try-catch

app.get('/', function (req, res, next) {
  setTimeout(function () {
    try {
      throw new Error('BROKEN')
    } catch (err) {
      next(err)
    }
  }, 100)
})

Promises

app.get('/', function (req, res, next) {
  Promise.resolve().then(function () {
    throw new Error('BROKEN')
  }).catch(next) // Errors will be passed to Express.
})

Error Handler

If any route throws an error, it will go to first error handler which has specifically 4 args:

app.use(function(error, req, res, next) {
  res.json({ message: error.message });
});
  • Can check type of error and respond with proper HTTP status

  • Can define multiple error handlers and chain with next

  • define last

  • Should either call next or respond, final call will be to default error handler

const { AssertionError } = require('assert');
const { MongoError } = require('mongodb');

app.use(function handleAssertionError(error, req, res, next) {
  if (error instanceof AssertionError) {
    return res.status(400).json({
      type: 'AssertionError',
      message: error.message
    });
  }
  next(error);
});

app.use(function handleDatabaseError(error, req, res, next) {
  if (error instanceof MongoError) {
    return res.status(503).json({
      type: 'MongoError',
      message: error.message
    });
  }
  next(error);
});

Catch all

function errorHandler (err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
}

app.use(errorHandler);

Edge Case

If streaming resp and you get an error, default error handler properly closes the connection and fails the request. So you should delegate to it, if headers have already been sent to the client:

function errorHandler (err, req, res, next) {
  if (res.headersSent) {
    return next(err)
  }
  res.status(500)
  res.render('error', { error: err })
}

Last updated