JDR JDR - 2 months ago 46
TypeScript Question

TypeScript and Koa 2: async/await issue with Global Error Handler

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;
}
}));


The question is two-fold:




  1. Is my application unable to catch thrown errors because of the transpilation? I mean, would it work if the transpiled JavaScript would make use of the
    async
    and
    await
    keywords, rather than transpiling it to generators using
    yield
    ?

  2. If the above is correct: is there any way to write Koa 2 apps with TypeScript now?



Edit



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]);
}

JDR JDR
Answer

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);
    });
}
Comments