One of the great things about error handling in Koa is that, by default, the framework handles all errors, either asynchronous or synchronous. This is made possible by the fact that Koa has a cascading middleware stack and an error handler can be added at the very top of the stack, which will unwind last. This makes it possible for Koa to handle all uncaught errors in applications by default.
Koa's default behavior is to output all errors to stderr unless app.silent is set to true.
To catch errors that occur in Koa, you can define an error-handling middleware to run as one of the first middleware. This is in contrast to Express, where error-handling middleware has to be defined as the last in the stack, with the signature (err, req, res, next).
In Koa, error-handling middleware can be defined as any other middleware, with the notable exception that it has to be registered as one of the first middleware. This ensures that the handler catches all errors in subsequent middleware.
A simple handler can be defined as seen as follows:
// catch all error in preceding middleware
app.use(async (ctx, next) => {
try {
await next();
} catch(err) {
ctx.status = err.status || 500;
ctx.body = err.message;
}
});
// throw error in response middleware
app.use(async ctx => {
throw new Error('An error occurred');
ctx.body = 'Hello, world';
});
In the code block, first, we define and register a middleware that wraps the call to the next middleware function in a try block. Hence, all errors that are thrown in subsequent middleware cascade up and will flow into the corresponding catch block.
Running the preceding code in a Koa app and visiting any route in the app will write a text response back to the client with the An error occurred message:
curl http://localhost:1234
// => An error occurred
Koa makes it possible to use simple try... catch statements for error handling, unlike in Express where the error in asynchronous functions has to be explicitly passed to the next middleware, using the next() method.
Errors can also be caught and transformed by implementing error-handling middleware. This is particularly useful for transforming errors of a particular kind and can help to reduce multiple try... catch statements. Here is an example of this use case:
app.use(async (ctx, next) => {
try {
await next();
} catch(err) {
if (err.status === 409 || err.statusCode === 409) {
err.message = "Conflict with current data exists!"
}
throw err;
}
});
In the preceding example, the error is modified and throw again to be handled by Koa's default response handler.