Error Handling with Express
Tags: node, webdev, programming, javascript
Published: 2021-01-25 01:26:03

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Like with any other apps, we have to make Express apps ready to handle errors like unexpected inputs or file errors.

In this article, we’ll look at how to handle errors with Express.

### Catching Errors

Error handling is the process of processing any errors that comes up both synchronously and asynchronously. Express comes with a default error handler so that we don’t have to write our own.

For example, if we throw errors in our route handlers as follows:

```
app.get('/', (req, res, next) => {
  throw new Error('error');
});
```

Express will catch it and proceed. We should see `error` instead of the app crashing.

For asynchronous errors, we have to call `next` to pass the error to Express as follows:

```
app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});
```

The code above will throw an error in the `setTimeout` callback and the `catch` block has the `next` call with the error passed in to call the built-in error handler.

We should see `error` instead of the app crashing.

Likewise, we have to catch rejected promises. We can do it as follows:

```
app.get('/', (req, res, next) => {
  Promise
    .reject('error')
    .catch(next)
});
```

Or with the `async` and `await` syntax, we can write the following:

```
app.get('/', async (req, res, next) => {
  try {
    await Promise.reject('error');
  }
  catch (ex) {
    next(ex);
  }
});
```

We should see `error` displayed instead of the app crashing with the stack trace.

The same logic also applies to routes with a chain of event handlers. We can call `next` as follows:

```
app.get('/', [
  (req, res, next) => {
    setTimeout(() => {
      try {
        throw new Error('error');
      }
      catch (ex) {
        next(ex);
      }
    })
  },
  (req, res) => {
    res.send('foo');
  }
]);
```

We should see the `error` displayed with the stack trace.

### Default Error Handler

The default error handler catches the error when we call `next` and don’t handle it with a custom error handler.

The stack trace isn’t displayed in production environments.

If we want to send a different response than the default, we have to write our own error handler.

The only difference between route handlers, middleware and error handlers is that error handler has the `err` parameter before the request parameter that contains error data.

We can write a simple route with a custom event handler as follows:

```
app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

app.use((err, req, res, next) => {
  res.status(500).send('Error!')
})
```

Note that we have the error handler below the route. The order is important. It has to below all the routes that we want to handle with it so that the error handler will get called.

We can write more than one custom error handler as follows:

```
app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

app.use((err, req, res, next) => {
  if (req.foo) {
    res.status(500).send('Fail!');
  }
  else {
    next(err);
  }
})

app.use((err, req, res, next) => {
  res.status(500).send('Error!')
})
```

What we have above is that if `req.xhr` is truthy in the first error handler, then it’ll send the `Fail!` response and not proceed to the second one. Otherwise, the second one will be called by calling `next` .

So if we add `req.foo = true` before the `setTimeout` in our route handler to have:

```
app.get('/', (req, res, next) => {
  req.foo = true;
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});
```

Then we get `Fail!` . Otherwise, we get `Error!` .

Calling `next` will skip to the error handler even if there’re other route handlers in the chain.

### Conclusion

To handle errors, we should call `next` to delegate the error handling to the default event handler if no custom event handler is defined. 

We can also define our own error handler function by creating a function that has the `err` parameter before, `req` , `res` , and `next` . The `err` parameter has the error object passed from `next` .

Error handlers have to be placed after all the regular route handling code so that they’ll get run.

Also, we can have multiple error handlers. If we call `next` on it, then it’ll proceed to the next error handler.