Alon Alon - 5 days ago 6
TypeScript Question

Catching unhandled exceptions in Express when using async/await

Regard the following TypeScript code:

app.get('/test_feature', function (req: Request, res: Response) {
throw new Error("This is the bug");
});

app.use(logErrors);

function logErrors (err: Error, req: Request, res: Response, next: NextFunction) {
console.log(err);
mongoDal.log(err.message, err);
next(err);
}


Here, I throw an error in a requests handler, and it fires the logErrors function as expected.

But then, I change my code to consume an async function:

app.get('/test_feature', async function (req: Request, res: Response) {
throw new Error("This is the bug");
await someAsyncFunction();
});


Now, because my function is async, the error somehow gets handled by the default error handler of Express, so my custom error handler doesn't get reached, nor the Node default error handler:

process.on('uncaughtException', function (err: Error) {
try {
console.log(err);
mongoDal.log(err.message, err);
} catch (err) {

}
});


How can I make my 'logErrors' function reached when an error occurs in an async function? I want a generic solution, not to try/catch in every requests handler.

Answer

The problem here is that your handler isn't throwing a synchronous exception at all any more. Instead, your handler returns a promise, which gets rejected. Note that this isn't a promise or async/await specific problem, this is a general issue for any express code using callbacks etc too - if you don't handle errors carefully everywhere when writing async code, it's easy to lose them completely.

To handle this in your case, something needs to register itself to catch rejections from the promise that you're returning. There's a few options for that:

  1. Explicitly add a .catch() to all your error handlers, and handle errors yourself, or by calling next(err) to delegate to the normal express error handling.
  2. Create a wrapping function for your handler to do this, and use it everywhere. You could use an existing wrap() function like express-promise-wrap for this.
  3. Extend .get and friends to automatically track rejections in promises returned from handlers. You could do this by hand, but it looks like express-as-promised is a working implementation of this (although I haven't tried it).

It's a little more complicated to set up, but 3 is strongly preferably in my opinion once you've got it in place. With that, you should be able to just write async functions as handlers, they'll all be returning promises under the hood, and your handler code will automatically monitor those promises for any subsequent failure.

StrongLoop have actually got an article looking at this generally in more detail, if you want some further reading: https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/

Comments