I am writing an app with TypeScript as well as Koa 2.
However, the issue I am experiencing is that my global Koa error handler does not catch errors that were thrown in my application.
Take the following middleware, for example (this is the very first middleware before any routes are loaded):
app.use(async(ctx, next) => {
console.log("ErrorHandler loaded...");
try {
console.log("Trying for error...");
await next();
} catch (err) {
console.log("Caught error...");
ctx.status = err.status || 500;
ctx.response.body = "Error: " + err.message;
}
});
When accessing my routes, I can see that the error handler is loaded and that the try
block runs.
However, if I throw an error in a route (irrespective of whether I use throw
or ctx.throw
), all I get is the default error message "Not found" - so, any errors I throw are never caught and, therefore, my error handler won't handle it.
Now consider the following transpiled JavaScript:
app.use((ctx, next) => __awaiter(this, void 0, void 0, function* () {
console.log("ErrorHandler loaded...");
try {
console.log("Trying for error...");
yield next();
}
catch (err) {
console.log("Caught error...");
ctx.status = err.status || 500;
ctx.response.body = "Error: " + err.message;
}
}));
async
and await
keywords, rather than transpiling it to generators using yield
?I found a "solution" to get my errors to throw, but I still don't understand why Koa didn't catch them.
This for of
loop iterates an array of controllers which I dynamically load. For each controller, I am attaching the respective method (action.method) of a class (action.target) to a route (action.route) using the specified Http verb (action.type).
However, I am also binding the context to the method so as to ensure that, as per Koa's conventions, this
is bound to the Context
:
for (let action of actionMetadata) {
router[action.type.toLowerCase()](action.route, (ctx, next) => {
(new action.target)[action.method].bind(ctx)(ctx, next);
});
}
The above causes the issue where errors are not caught.
In comparison, the below code works: errors are now caught by Koa. But this means that this
now is not the Context
anymore.
I can live with that since the Context
is the first param in my routes, but I don't understand why the error is not caught as all middleware following the error handler (which is my very first middleware) should run within its try
block:
for (let action of actionMetadata) {
router[action.type.toLowerCase()](action.route, (new action.target )[action.method]);
}
Right, looks like this was pure user error in not ensuring that every single middleware returns a promise (or is an async function).
The async
in the following is all that was missing:
for (let action of actionMetadata) {
router[action.type.toLowerCase()](action.route, async (ctx, next) => {
(new action.target)[action.method].bind(ctx)(ctx, next);
});
}