I am trying to write my own node.js server, so far I have a question.
const asyncHandler = (fn) => async (req, res, next) => {
try {
if (!fn) next();
await fn(req, res, next);
} catch (e) {
next(e);
}
}
const errorHandler = (error, req, res, next) => {
if (!error.code) {
error = new InternalServerError(error.message ? error.message : 'Something broke');
}
const errorResponse = new MessageResponse(error.code, error.message);
res.status(errorResponse.code).send(errorResponse.body);
}
These are two middlewares, the first one handles promise rejection the second one is express error handler. I am using first one like this
router.get('/', asyncHandler(async (req, res, next) => {
let tasks = await TaskService.getAllTasks();
tasks = tasks.map(task => {
return task.entitize();
})
}));
So I need to wrap all route middleware function into an async handler to handle the rejection. But what if I had 10000 route middleware functions? Can I write some code to wrap all route middleware functions into this async handler BY DEFAULT? I mean writing like this
router.get('/', async (req, res, next) => {
throw new Error('aaa');
let tasks = await TaskService.getAllTasks();
tasks = tasks.map(task => {
return task.entitize();
})
});
And this middleware is wrapped by asyncHandler? Has anyone done it before? Are there any libs I can use to achieve this? Thx in advance.
You could wrap the original router.get
function and do your async/error handling stuff there along with delegating to the original function. Something like this should work:
const router = express.Router();
const _route = router.route.bind(router);
const methodsToWrap = ['get', 'post', 'patch', 'delete'];
router.route = function(path) {
const route = _route(path);
for (const method of methodsToWrap) {
if (route[method]) {
route[method] = wrap(route[method]);
}
}
return route;
};
function wrap(originRouterMethod) {
return function() {
const originMiddlewares = [...arguments];
const wrappedMiddlewares = originMiddlewares.map(fn => {
if (typeof fn !== `function`) {
return fn;
}
return async function(req, res, next) {
try {
await fn.apply(null, arguments);
} catch (err) {
console.log('Caught error ' + err);
next(err);
}
};
});
originRouterMethod.call(this, wrappedMiddlewares);
};
}
With that you don't need to add asyncHandler
to all your routes in your setup, you can just specify them as:
...
router.get('/', async(req, res, next) => {
let tasks = await TaskService.getAllTasks();
tasks = tasks.map(task => {
return task.entitize();
});
});
...