Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 339
Alon Avatar asked Nov 28 '16 17:11

Alon


People also ask

Which is the proper way to handle asynchronous errors in Express?

To handle an error in an asynchronous function, you need to catch the error first. You can do this with try/catch . Next, you pass the error into an Express error handler with the next argument. If you did not write a custom error handler yet, Express will handle the error for you with its default error handler.

How do you handle promise rejection with await?

You can handle rejected promises without a try block by chaining a catch() handler before awaiting the promise.

Does express support async await?

Using async/await in ExpressJs controllers We can convert this into a function that uses async/await in Express by first marking the handler as an async function. Then we can call await inside of the request handler.


1 Answers

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/

like image 133
Tim Perry Avatar answered Nov 03 '22 02:11

Tim Perry