WittCode💻

Will Your Express App Crash?

By

Learn how to handle errors on an Express server to prevent it from crashing.

Table of Contents 📖

Express Default Error Handling

Express has built in error handling for both synchronous and asynchronous errors. However, the way Express handles these errors is slightly different. If we throw an error synchronously inside a middleware function it won't crash the program.

app.get('/', (req, res) => {
  throw new Error('woops!');
});
curl localhost:1234/
Error: woops!
    at <anonymous> (webpack:///src/server.js:14:1)
    
curl localhost:1234/
Error: woops!
    at <anonymous> (webpack:///src/server.js:14:1)

Here, Express will use its default error handling middleware to send the error back to the client and also log it to the server console. However, asynchronous errors are handled differently. If we throw an error inside an asynchronous function it will crash the program.

app.get('/', async (req, res) => {
throw new Error('woops!');
});
curl localhost:1234/
[nodemon] app crashed - waiting for file changes before starting...

INFO: It should be noted that Express version 5 will handle asynchronous errors just like synchronous errors.

Express next Function and Errors

To have Express handle asynchronous errors we need to use the Express next function. This function passes control to the succeeding, or next, middleware. If we pass an argument to the next function, then it will pass control to the next error handling middleware.

app.get('/', async (req, res, next) => {
  try {
    throw new Error('woops!');
  } catch (err) {
    next(err);
  }
});
curl localhost:1234/
Error: woops!
    at <anonymous> (webpack:///src/server.js:14:1)
    
curl localhost:1234/
Error: woops!
    at <anonymous> (webpack:///src/server.js:14:1)

Now our application won't crash when an error is thrown inside an asynchronous function. Note that anything passed to the next function will be treated as an error.

app.get('/', async (req, res, next) => {
  next('something went wrong')
});
curl localhost:1234/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>something went wrong</pre>
</body>
</html>

If we don't pass anything to the next function then the following middleware will be called.

app.get('/', async (req, res, next) => {
  try {
    throw new Error('woops!');
  } catch (err) {
    return next();
  }
});

app.use((req, res, next) => {
  return res.sendStatus(200);
});

ERROR: There is a catch to this rule though as if the string route is passed to the next function then it will treat it as though nothing has been passed and consecutive middleware functions will be called.

Custom Express Error Handling Middleware

The built in Express error middleware runs at the end of the middleware function stack, sends the error to the client, and writes the stack trace to the console. If we want to do something else, we can create our own custom error handler. To make custom error handler middleware, we need to provide the middleware function with 4 arguments: the error, then request, then response, and then next function.

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

From this custom error handler, we can still trigger the default error handler if we call next and pass an error.

app.use((err, req, res, next) => {
  next(err);
});

If we don't respond to the client, then the request will hang indefinitely. Finally, like the Express default error handling middleware, this middleware should be placed last in the middleware chain.

WARNING: Like the Express default error handling middleware, this middleware should be placed last in the middleware chain.