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');
}
)};
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'm using paypal-node-SDK library to make calls to Paypal API. It uses a promise like this:
paypal.payment.create(create_payment_json, function (error, payment) {
if (error) {
throw error;
} else {
console.log("Create Payment Response");
console.log(payment);
}
});
However I'm trying to make it async because my other functions are async/await as well. But it doesn't return any callback, just undefined.
exports.create = wrap(async(req, res) => {
const payment = await paypal.payment.create(create_payment_json);
});
//wrap
module.exports = (fn) => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch((error) => {
console.log(error);
res.status(400).send({success: false, message: error.message});
});
};
};
It seems the library supports promises (feature: link), but the cb(null, response) doesn't really return anything when it is a async function. Am I missing something? Is there a way to make it work async?
You need beta / version 2.0+ to use promises in the sdk.
Not sure exactly what your wrap is, but for node styled callbacks function foo(a,b,callback) you can use promisify
const { promisify } = require('util');
exports.create = promisify(paypal.payment.create);
A manual conversion of paypal.payment.create would be
function create(create_payment_json){
return new Promise(function(resolve,reject){
paypal.payment.create(create_payment_json,function(error,payment){
if(error){
reject(error);
}else{
resolve(payment);
}
}
};
}
Which can then be used by const payment = await create(json);
Then in your router you can use something like
router.get('/', async function (req, res, next) {
try{
const payment = await create(json);
res.send(payment);
}catch(e){
console.log(e);
}
});
Extending on the answer by Cody G..
It is correct you need to upgrade to the beta v2+ to use promises in the sdk, although if you wish to do so, after you've upgraded you will find there are breaking changes.
You can read the full documentation here:
https://github.com/paypal/PayPal-node-SDK/tree/2.0-beta
There is also a migration guide to easily transition from v1 to v2:
https://github.com/paypal/PayPal-node-SDK/blob/2.0-beta/docs/Migrating.md
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).
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)