I have router and two acync fetching from DB. What is the correct way to catch errors here?
router.get('/restaurant/:id', async (req, res, next) => {
var current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec();
var products = await Product.find({restaurant: req.params.id}).exec();
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
});
Either:
try {
var current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec();
var products = await Product.find({restaurant: req.params.id}).exec();
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
} catch (err) {
// Handle your errors here...
}
which is your typical pattern. Depending on if you can recover and continue with the rendering (with a default value or something), you might want to enclose only the await calls in the try block (and declare the variables before the try block).
Otherwise, you're awaiting promises, so you can still use the promise .catch method if that seems cleaner to you.
var current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec()
.catch(err => { /* handle error here*/ });
var products = await Product.find({restaurant: req.params.id}).exec()
.catch(err => { /* handle error here*/ });
However, this pattern is only going to be useful if you can still return something valid in your catch code (eg, some kind of default or fallback value). If you need to bail out of the whole thing in the event of an error, this won't work for you, and you should use the previously mentioned try/catch pattern instead.
If you're going to use await, then you would use try/catch to catch a rejection of either of the promises you were awaiting:
router.get('/restaurant/:id', async (req, res, next) => {
try {
let current_restaurant = await Restaurant.findOne({restaurantId: req.params.id}).exec();
let products = await Product.find({restaurant: req.params.id}).exec();
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
} catch(e) {
// do whatever you want
console.log(e);
res.sendStatus(500);
}
});
You can compare that to regular promise programming using .then():
router.get('/restaurant/:id', async (req, res, next) => {
Promise.all([
Restaurant.findOne({restaurantId: req.params.id}).exec(),
Product.find({restaurant: req.params.id}).exec()
]).then([current_restaurant, products] => {
res.render('restaurant', {
user: req.user,
csrfToken: req.csrfToken(),
current_restaurant: current_restaurant,
products: products
});
}).catch(e => {
// add your desired error handling here
console.log(e);
res.sendStatus(500);
});
});
Since your two database requests are independent of one another, the second version allows the DB queries to be parallelized and may actually run slightly faster (depends upon your database).
Related
I have the following GET call which works as intended, connecting to a couchbase db and performing some updates.
databaseRouter.put('/update/:id', (req, res) => {
updateDocument(req, res);
});
export const updateDocument = (req, res) => {
collection.get(req.params.id, (err, result) => {
if (err) {
res.status(404).send(err);
} else {
const document = result.value;
document.product_id = req.body.id || document.product_id;
collection.replace(req.params.id, document, (err) => {
if (err) {
res.status(500).send(err);
}
}).then(() => res.json(document));
}
}).catch(e => console.log(e));
}
This is for external clients to use.
But I want this to logic to be reusable within the project in another instance for batch processing. Not a rest call.
Thus I am looking to refactor the updateDocument function to return the document value or errors instead of performing res.send();
But I can't just modify as follows. result is undefined.
And I am also not gonna be able to maintain the status codes for errors.
Unless I explicitly return like a object with a key called status.
export const updateDocument = (req, res) => {
.....
}).then(() => document); // instead of }).then(() => res.json(document));
.....
}
databaseRouter.put('/update/:id', (req, res) => {
const result = updateDocument(req, res); // result is undefined
res.send(result);
});
Is there a way I could elegantly extract the logic so that I can continue to achieve what I have for the GET call for clients
but also be able to reuse the same logic internally within the project?
I am creating a document in my MongoDB database using Mongoose with the following code:
workoutsRouter.post('/', async (req, res) => {
await mongoose.connect('mongodb+srv://nalanart:<password>#cluster0.2iplh.mongodb.net/workout-app-db?retryWrites=true&w=majority',
{ useNewUrlParser: true, useUnifiedTopology: true })
await Workout.create({
mains: req.body.mains,
accessories: req.body.accessories
}, err => {
if(err) {
throw err
} else {
res.sendStatus(201)
}
})
})
My problem is that it does not send a 201 status as I have written it to. Any idea why? It just says Sending... on Postman
And here it appears in my collection:
Yeah, because you are both awaiting and passing a callback. You are mixing the await and callback syntaxes. If you pass Mongoose a callback, it won't return a Promise, so it will be awaited forever, since it will never resolve. Either await it, or pass it a callback, not both. Also, try res.status(201).end()
try {
await Workout.create({
mains: req.body.mains,
accessories: req.body.accessories
});
res.status(201).end();
} catch (err) {
throw err
}
I have the following function:
exports.signup = async(req, res) => {
console.log('signup');
const user = new User({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8)
});
try {
if (await user.save()) {
if (isNonEmptyArray(req.body.roles)) {
// How do I catch this error? can be a role error or db error
const roles = await Role.find({name: { $in: req.body.roles }}).exec()
user.roles = roles.map(role => role._id);
if (await user.save()) {
success(res, 'Registered!');
}
} else {
// How do I catch this error? can be a role error or a db error
const role = await Role.findOne({name: 'user'}).exec();
user.roles = [role._id];
if (await user.save()) {
success(res, 'Registered!');
}
}
}
} catch(error) {
fail(res, {message: 'Database internal error occured.'});
}
};
Is it correct that the catch will trigger for all errors in the block including calls to await Role.find({name: { $in: req.body.roles }}).exec()? How would I catch this error independently? Do I need to add a try and catch within the try and catch statement?
like you said you can use another try-catch block to distinguish which error are you catching.
try {
const role = await Role.findOne({name: 'user'}).exec();
} catch(err) {
console.log(err);
}
Another idea might be to use the promises and catch the error in each .catch segment, for example
var query = Role.findOne({name: 'user'});
query.exec().then(function () {
// handle success
}).catch(function (err) {
// handle error
});
anyway there are some important to feature to keep in mind when using async/await and try-catch block, I'll put here the conclusion of an article and the link to it if you are interested:
conclusion:
We can use try...catch for synchronous code.
We can use try...catch (in combination with async functions) and the .catch() approaches to handle errors for asynchronous code.
When returning a promise within a try block, make sure to await it if you want the try...catch block to catch the error.
Be aware when wrapping errors and rethrowing, that you lose the stack trace with the origin of the error.
font: https://itnext.io/error-handling-with-async-await-in-js-26c3f20bc06a
I'm new to promises. And I'm trying to use them with mongoose query functions like find() and findById(). Everything seems to work but I'm not sure if this is the correct way of chaining then. The objective of using promises is to eliminate callback hell but the way I'm chaining then looks very similar to callbacks. Is there a better way to write this route?
router.get('/homehr/employees/new', middleware.isLoggedInAsHR, (req, res) => {
Department.find({})
.exec()
.then((allDepartments) => {
Employee.findById(req.user.employee.id)
.exec()
.then((foundEmployee) => {
res.render('hr/employees/new', {
departments: allDepartments,
employee: foundEmployee,
blogs: allBlogs
});
});
})
.catch((err) => {
console.log(err);
req.flash('error', err.message);
return res.redirect('back');
});
});
Your Routes doesn't seems to have dependency of fetching models in sequence. So You can write this in more better way as follow:
router.get('/homehr/employees/new', middleware.isLoggedInAsHR, async (req, res) => {
try{
const allDepartments = await Department.find({});
const foundEmployee = await Employee.findById(req.user.employee.id);
res.render('hr/employees/new', {
departments: allDepartments,
employee: foundEmployee,
blogs: allBlogs
});
}catch(err){
console.log(err);
req.flash('error', err.message);
return res.redirect('back');
}
)};
I'm using promises inside express middleware. I want to use the async/await methods.
app.get('/data1',async function(req,res) {
data = await getData1(); // This line throw an error,
res.send(data)
})
app.get('/data2',async function(req,res) {
data = await getData2(); // This line throw an error
res.send(data)
})
This makes the browser wait forever.
On the server I see
(node:251960) UnhandledPromiseRejectionWarning: Unhandled promise rejection
Now, to fix it for one middleware I'm doing:
app.get('/data1',async function (req,res){
return (async function(){
data = await getData1()
})().catch(() => {
res.send("You have an error")
}
})
app.get('/data2',async function (req,res){
return (async function(){
data = await getData2()
})().catch(() => {
res.send("You have an error")
}
})
I don't like this repetion. How can I set default error? I have tried for example:
app.use(function(error,req,res,next)){
res.send('You have an error')
}
But it didn't work.
In other words: How to set default function to be called when Express middlewares returning a rejected promise?
Now I found a way how to do it, I'm still keep the question open for more suggestions
app.get("/data1",
wrap_middleware(async (req, res) => {
data1=await getData1()
res.send(data1)
})
}
app.get("/data1",
wrap_middleware(async (req, res) => {
data2=await getData2()
})
}
function wrap_middleware(func) {
return async (req, res, next) => {
func(req, res, next).catch(err => {
console.log(err.message);
res.send("Error");
});
};
}
I don't understand the use of sending the same error for different function but I think the handling error code could be write in more readable way (just catch the error and do with them what you want the same way you catch errors in any route middleware):
function getData1(){
return new Promise( (resolve,reject) => {
setTimeout(() =>{
reject(new Error('error has occur!'));
},2000);
});
}
router.get('/data1', async (req,res, next) => {
try{
const data = await getData1();
res.send(data);
}
catch(ex){
res.send(ex.message);
// next(ex); => sending it to express to handle it
}
});
If you want a global error handling then its not any different from any code you want catch errors globally - you can set a function that take as param , the response object and the async code and create general catch for every async call comes from middleware (which has response object)