Search code examples
javascriptnode.jsexpresses6-promise

Express: next is not called properly inside a catch after await


I have a very simple route

router.get('/', async (req, res, next) => {
  try {
    const allEmployees = await employees.find({});
    res.json(allEmployees);
  } catch (error) {
    next(error);
  }
});

It works ok. But after I refactored it with catch. it stopped working and threw UnhandledPromiseRejectionWarning:

router.get('/', async (req, res, next) => {
  const allEmployees = await employees.find({}).catch(next)
  res.json(allEmployees);
});

It seems like the next is not called correctly in the second version. But Both should be equivalent in JavaScript. not sure why the second one is broken in Express.


Solution

  • Both should be equivalent in JavaScript

    No, they are not.

    Promise.prototype.catch() returns a new promise that resolves with the return value of the callback. Since next() returns void, this code...

    employees.find({}).catch(next)
    

    returns a successful promise, resolving with undefined. next() will be called with the failure but there's nothing stopping the rest of your code from calling res.json(undefined).

    If you want to use the Promise prototype methods, the equivalent would be

    router.get('/', (req, res, next) => {
      employees.find({}).then(res.json).catch(next);
    });
    

    If you wanted to keep using async / await and have the eventual promise fail, you'd need something like this

    router.get("/", async (req, res, next) => {
      const allEmployees = await employees
        .find({})
        .catch((err) => Promise.reject(next())); // rejected result
      res.json(allEmployees);
    });