Reputation: 640
Environment: node.js, Express
I'm attempting to use an error handling pattern based on work done by Valeri Karpov (creator of Mongoose). He explains the pattern that he uses in this article, The 80/20 Guide to Express Error Handling.
In the simplified server below I can successfully feed errors through to my error handling middleware.
const express = require('express');
const app = express();
app.set('view engine', 'ejs');
app.get('/', async function(req, res, next) {
let catStatus = false;
function answer(X) {
return new Promise( function(resolve, reject) {
if(X) {
resolve('cat does exist');
} else {
reject('whoops, cat does not exist');
}
});
}
answer(catStatus)
.then( function(data) {
res.render('index', { output: data });
})
.catch( function(error) {
next(new Error(error));
});
});
app.use( function(error, req, res, next) {
res.render('error', { error });
});
app.listen(8080, function(){
console.log('listening on port 8080');
});
However I'm stuck on how to implement his wrapper pattern with my basic setup. Does the code inside my '/'
endpoint go inside of his '*'
endpoint?
If so does anything go inside of his function wrapAsync(fn)
? Should I just delete his 2 comment lines and leave it as is?
app.get('*', wrapAsync(async function(req, res) {
await new Promise(resolve => setTimeout(() => resolve(), 50));
// Async error!
throw new Error('woops');
}));
app.use(function(error, req, res, next) {
// Gets called because of `wrapAsync()`
res.json({ message: error.message });
});
app.listen(3000);
function wrapAsync(fn) {
return function(req, res, next) {
// Make sure to `.catch()` any errors and pass them along to the `next()`
// middleware in the chain, in this case the error handler.
fn(req, res, next).catch(next);
};
}
Upvotes: 1
Views: 2830
Reputation: 1318
I think the *
endpoint in app.use()
is just a wildcard saying to route all incoming requests to that code, similar to the way your /
is working.
But yes, you are understanding correctly. Basically, he is saying that any middleware which makes asynchronous requests should use this wrapAsync
function. You can remove the comment lines in the wrapAsync
implementation if you want.
With the wrapAsync
function, you can pass your own async middleware functionality into this wrapper, and it will make sure to call your async middleware and .catch()
the promise to call next
, so that you don't have to worry about that detail when writing code. You can simply throw errors, and then async wrapper will handle the Express requirement of calling next()
when async code fails.
app.use("/", (req, res, next) => {
verifyRequestorPermissionsAsync(req.params)
.catch(err => {
return next(err);
});
});
With the wrapAsync
middleware, you will automatically call next with the rejected error (if there is one), so you can clean up the code quite a bit (as well as avoid accidentally forgetting to call next on a rejection).
app.use("/", wrapAsync(async(req, res, next) => {
await verifyRequestorPermissionsAsync(req.params);
}));
These two docs / articles will help clear things up a lot, if I am guessing correctly where the confusion is coming from:
next
and passing a value into that function: https://expressjs.com/en/guide/error-handling.htmlUpvotes: 1