Handle Syncronous and Asyncronous Errors in Express

Share this video with your friends

Social Share Links

Send Tweet

In this lesson, we'll look at how to handle errors in syncronous and asyncronous request handler functions.

We'll also look at custom error middleware that allows for better control over how we return errors to our client based on whether we are in a production or development environment.

Kevin Cunningham: [0:00] I have a test route, and all it's doing is throwing a new error. If I visit that within my client, I get error, my error message, my stack trace, and my status of 500.

[0:12] If I have read this as an asynchronous request handler, instead of having my error message resolve to the client, it will not be passed back. Express, by default, does not handle asynchronous errors. We need to handle those ourselves.

[0:26] There are a number of ways to do that. The first is we can use the optional next parameter inside of the request handler. Instead of throwing the new error, we can return next with the error. This provides the same developer experience with the error and the status message, but we need to remember to do this at every point where our application could possibly throw an error.

[0:51] Another approach is to use try-catch where we can have any amount of errors thrown within our try. When we catch an error, we pass that to our next handler. Again, we get the same developer experience.

[1:04] I prefer to use a library like express-async-errors. That means I don't have to include try-catch in all the places where I want to cover asynchronous errors, and I don't have to use a next handler. That gives me the same user experience where I have the error message, the stack trace, and the 500 status error.

[1:25] However, that experience isn't as nice as I would like it to be. The error message is ugly, and I'd like to be able to have more control over my errorHandling. I've got this errorHandling middleware. The only difference an errorHandling middleware is it has an error as its first parameter as well as the request, the response, and next.

[1:46] The first thing I do is check whether or not res.headersSent exists. If it does, it means that the response has already been sent, so I call next(error); because I don't need to do anything. Otherwise, I set the status to 500, and I start making this JSON object I'm going to pass back with the error message. If I'm in production, I'm going to send back nothing else.

[2:08] However, if I'm not in production, I'm going to send back the stack and the errors of the stack trace. Now I need to use this. The first thing is I'll import it. Then I need to tell my app to use it. It's important that the last thing I ask my app to use before I call listen, so app.use(errorHandling);.

[2:31] Now when I get an error, I get a much nicer and cleaner experience. I get a JSON object, which is easier to parse than the plain text that was being sent back before.

[2:40] If I was in production, I wouldn't get the stack trace at all. If I just change this Boolean, we can see that I would just get the message. I wouldn't get anything else, whereas if I'm not in production, I get a helpful message and the stack.