简体   繁体   中英

Express.js: Pass asynchronous errors thrown

I notice a recurring pattern in my express app which I think could be optimized. Basically I have a route calling a method with some asynchronous functions.

index.js

const controller = require('./controller.js');

const router = new Router();

router.post('/user', controller.createUser);

module.exports = router;

controller.js

exports.createUser = async (req, res, next) => {
    try {
        // asynchronous calls, etc.
    } catch (e) {
        // pass error to middleware
        next(e);
    }
}

The try/catch blocks are recurring in each of my controller methods. I'd want errors caught to be passed to my error-handling middleware. Therefore it seems impractical and repetitive to pass errors in each of my controller functions. Could I refactor this?

What if I wrap the controller method in a function as such:

index.js

const controller = require('./controller.js');

const router = new Router();

const handleErrors = (func) => async (req, res, next) => {
  try { await func(req, res, next) }
  catch (e) { return next(e) }
};

router.post('/user', handleErrors(controller.createUser));

module.exports = router;

controller.js

exports.createUser = async (req, res, next) => {
    // asynchronous calls, etc.
    if (a !== b) {
        // errors can be passed to middleware as such
        throw new CustomError(400, 'a is not equal to b');
    }
}

Would this be an appropriate solution? Does Express have any built-in ways of accomplishing the same thing? Should I be cautious about refactoring my entire application in this way?

Would this be an appropriate solution?

Yes, looks nice.

Does Express have any built-in ways of accomplishing the same thing?

No, Express was written before async / await was introduced.

Should I be cautious about refactoring my entire application in this way?

I don't think so. How i would write that:

const handleErrors = (func) => (req, res, next) => func(req, res).then(() => next(), next);

I recommend you this article: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016

As in the article, this should be the middleware:

const asyncMiddleware = fn =>
  (req, res, next) => {
    Promise.resolve(fn(req, res, next))
      .catch(next);
  };

This is how a controller should look like:

router.get('/users/:id', asyncMiddleware(async (req, res, next) => {
    /* 
      if there is an error thrown in getUserFromDb, asyncMiddleware
      will pass it to next() and express will handle the error;
    */
    const user = await getUserFromDb({ id: req.params.id })
    res.json(user);
}));

router.post('/users', asyncMiddleware(async (req, res, next) => {
  const user = await makeNewUser(req.body);
  res.json(user)
}))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM