A WatchMeCode subscriber recently asked a question about error handlers with Node and Express.
In his scenario, the subscriber wants to have a series of routers to handle requests to /api. Within these routes, he wants a generalized error handler that will return the error as a JSON document instead of rendering an HTML page as the default handler does. So, how should this be done?
The question is as follows:
What are your thoughts on using router-level / router-specific error handlers?
Until now I’ve been modifying the generated error handlers, from express-generator, to detect if the requested path was prefixed with /api. In those situations I was passing back an error message instead of rendering the error view. However, this means that everything needs to be buried under /api… which is not always desirable.
The short answer is, Yes! Use router-specific error handler. The long answer needs a bit more dissection…
Error Handlers Are Just Middleware
When you run the Express generator, it provides a sample for a default error handler in the app.js file, similar to this:
When you look at this code with a bit of cleanup, you’ll see that error handlers are just middleware with a specific number of parameters for the callback function.
Since this is middleware, but has no route specified as the first parameter, it will usually be the last function to be called in the middleware stack of error handlers.
Knowing this provides a lot of power and flexibility for making use of router-specific error handlers, and facilitating the /api specific handlers to return JSON data instead of rendering a web page.
An Error Handler For /api
With an understanding of middleware and knowing that error handlers are just middleware, it’s easy to provide an error handler just for the /api routes. You only need to “use” a middleware error handler with the “/api” route passed as the first parameter:
Now any time an error is returned through a “next(err);” call from within any “/api” routes, this error handler will be executed. This error handler will send back JSON data, as an API would expect to receive, instead of trying to render HTML.
For any other routes that aren’t part of “/api”, the default error handler in the app.js file will still be used and will still render HTML as expected.
And if you find yourself sitting in the error handler for “/api” but you know the error should not be handled in this location, you can forward the error on up the middleware chain by calling “next(err);”.
This update to the previous error handler will forward the error to the next handler if the specified condition is true, allowing the default (or next) error handler to have a chance to work.
Global, Routes and Sub-Routes
Knowing error handlers are just middleware, like everything else related to routes and routers in Express, makes them very powerful and very flexible.
The default code from the Express generator command line tool will give you a “global” or last-ditch error handler. This is a good place to start for your error handler needs. And with a little bit of code and thought process, you can easily provide route and sub-route specific error handlers, as shown above.
Want To Keep Your Routers / Routes Clean?
With great flexibility comes the potential for a great mess – and Express routers are no exception to this. It’s easy to just cram routes into a single .js file and be done with it, but this quickly becomes unmaintainable. Add error handlers into the mix, and you’re only adding that much more to the mess.
So, what are the secrets to keeping routes, router files, error handlers, middleware and other bits of code in your Express app clean?