Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Express error handling and async await

In my Node.js app I added the following code to catch every uncaught exception:

process.on('uncaughtException', function (err: Error) {
    try {
        logger.err(err);
    } catch (err) {

    }
});

The problem is that Express has its own default error handler, which catches every uncaught exception. Now, Express caught the exceptions before Node (process.on), so my logger didn't get reached. However, it's possible to add another error handler that can catch every exception before Express does:

app.use(logErrors);

function logErrors (err: Error, req: Request, res: Response, next: NextFunction) {
    logger.err(err);
    next(err);
}

This still doesn't cover every case. Whenever I have an async function that I call with await, there's no exception, but a rejected Promise is returned instead. For example:

app.get('/foo', async function (req: Request, res: Response, next: NextFunction) {
    await bar();
});

function bar() {
    throw new Exception();
}

won't reach my logErrors function because it won't throw, but will return a rejected Promise.

So to fix it, I wrapped my Express HTTP handlers with another function:

app.get('/foo', wrap(async function (req: Request, res: Response, next: NextFunction) {
    await bar();
}));

function wrap(func: (req: Request, res: Response, next: NextFunction) => void) {
    return async function (req: Request, res: Response, next: NextFunction) {
        try {
            await func(req, res, next);
        }
        catch (err) {
            next(err);
        }
    }
}

next(err) passes the error to my handler. Now, I manage to catch the exceptions to my logErrors function.

I'm almost done. I still have one case in which I can't catch the errors. It happens when I call an async function without the await keyword (It's sometimes useful to use the same function in two different places in the code, once calling in asynchronously, and once synchronously). So this code won't catch the error:

app.get('/foo', wrap(async function (req: Request, res: Response, next: NextFunction) {
    bar();
}));

What's happening here is that the Express HTTP handler returns a resolved Promise to the wrap function. The wrap function in turn, does not reach the catch block, so it does not call to next(err) which would reach my logger.

bar function in turn, returns a rejected Promise, but there is no one waiting for its return value.

How can I change my code in such a way I won't end up with any unhandled Promise rejection? (only generic solution)

like image 516
Alon Avatar asked Jan 10 '17 13:01

Alon


1 Answers

There is another process.on event you can set up a listener for - unhandledRejection.

You can use it to handle those rejections all over the code.

NOTE: remember to terminate your process after you logged everything you needed. More on this here.

like image 166
Kirill Rogovoy Avatar answered Sep 23 '22 15:09

Kirill Rogovoy