Reputation: 4942
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?
Upvotes: 0
Views: 41
Reputation: 138297
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);
Upvotes: 2
Reputation: 603
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)
}))
Upvotes: 1