Improve callback code into async await mongoose - javascript

I want improve my old callbacks code in mongose with async/await methods (which are much better to read and organized)
PUT is the problem
I a have a findById, which are correctly
The problem is when try to update document with await user.save(userWithNewProps)
// put
app.put('/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id).exec()
if (user === null) return res.status(404).json(null)
const userWithNewProps = {name: 'Homer', lastame: 'Simpson'}
const userUpdated = await user.save(userWithNewProps) // Doesn't works!
res.status(200).json(userUpdated)
} catch (e) {
return res.status(500).json(e)
}
})
I tried to study many tutorials, and other questions, but is some difficult for me.
Can you check my error?
callback hell
This is the original callback code, works fine, but is the callback hell and demonic spirits:
// put
app.put(
'/:id',
(req, res) => {
User.findById(req.params.id, (err, userFound) => {
if (err) {
return res.status(500).json(err);
}
if (!userFound) {
return res.status(404).json(err);
}
userFound.name = 'Homer';
userFound.lastname = 'Simpson';
userFound.save((err, userUpdated) => {
if (err) {
return res.status(500).json(err);
}
res.status(200).json(userUpdated);
});
});
});
Many thanks

As I understand from our discussion in comments, the problem is in the updating and not when saving data, so you need to inform Mongoose's change tracking of the change.
Informing Mongoose about the changes can be handled by using the markModified() method and it should be before saving.
user.name = 'Homer';
user.lastname = 'Simpson';
user.markModified('name');
user.markModified('lastname');
await user.save();
Regards :)

Related

Trying to delete a doc Mongoose using findByIdAndDelete but im always getting back a null

Trying to delete a doc Mongoose using findByIdAndDelete but im always getting back a null when I know the doc id am passing to the function exists in MongoDB
I've tried:
findByIdAndRemove
findByIdAndDelete
findOneAndDelete -> this with the code below deletes two docs rather than one!
async function deleteCourse(id)
{
const result = await Course.findByIdAndDelete(id,(err, res) => {
if (err){
console.log(err)
}
else{
console.log("Deleted course: ", res);
}
})
}
deleteCourse('5a68fdf95db93f6477053ddd')
As #Yousaf suggested, you dont need the callback function if you are using async/await function. Also use try/catch to have the same behavior.
async function deleteCourse(id)
{
try {
const result = await Course.findByIdAndDelete(id);
console.log("Deleted course: ", result);
} catch(err) {
console.log(err)
}
}
deleteCourse('5a68fdf95db93f6477053ddd')

Multiple queries in NodeJS and MongoDB

I've got a NodeJS/Express/MongoDB app. For one of my endpoints I'm trying to get some stats from the database and I'm having trouble doing it using promises.
I know that db doesn't get moved between the different .thens but no matter how I re-arrange the code I can't get anything out of the console.log() except the first users count. I've also tried saving db into a variable declared at the start of the function but that doesn't help either.
What's the proper way to make multiple queries to MongoDB in a promisified way?
Node Code:
function getStats(num){
var stats = "";
MongoClient.connect(`${mongoString}/SiteUsers`)
.then(db => {
db.db().collection("Users").find({}).count()
.then( userCount => {
stats += `Users: ${userCount}\n`
return db;
})
.then( adminCount => {
db.db().collection("Users").find({admin:true}).count()
.then( adminCount => {
stats += `Admins: ${adminCount}\n`
})
})
.then( data => {
console.log(`Stats are now: \n${stats}`);
})
})
.catch(err => {
if(err) console.log(err);
});
}
Thanks!
There are several ways that you can handle the order of your promises. One of which is to use async/await functionality.
async function getStats(num){
try {
const conn = await MongoClient.connect(`${mongoString}/SiteUsers`)
const userCount = await conn.db().collection("Users").find({}).count()
const adminCount = await conn.db().collection("Users").find({admin:true}).count()
console.log(`User count: ${userCount}\nAdmin count: ${adminCount}`)
} catch (err) {
console.log(err)
}
}

How to use async await in nodejs

I made an api like below.
I think it works asyncrously well without doubt.
exports.query = async(req, res) => {
try{
const result = await user.findOne({})
res.send(result);
} catch(error) {
res.send(error)
}
}
But when I try this like below, I am not sure it works asyncrously or not.
exports.query = async(req, res) => {
try{
user.findOne({})
.then(async(result) =>{
order.findOne({id: result.id})
.catch(e => res.send(e)
} catch(error) {
res.send(error)
}
}
I have to attach 'await' in front of user.findOne({}) like below?
exports.query = async(req, res) => {
try{
await user.findOne({})
.then(async(result) =>{
Or it doesn't matter? That is, it works same asycrously, even though I don't write await in front to user.findOne using 'then'?
thank you so much for reading.
Generally, inside a single block, you should either use await, or use .then, but not both - similarly, using Promise.prototype.catch is really weird in combination with try/catch where you can already await.
For your exports.query to resolve once the second findOne finishes, await or return both Promises, and connect the inner findOne connected to the outer Promise chain. Consider something like this instead:
exports.query = async (req, res) => {
try {
const userResult = await user.findOne({});
const orderResult = await userResult.findOne({ id: userResult.id });
// do something with orderResult
} catch (error) {
res.send(error)
}
}
If you use .then and catch without await, then it would look like:
exports.query = (req, res) => {
return user.findOne({})
.then((result) => {
return order.findOne({
id: result.id
})
.then((orderResult) => {
// do something with orderResult
})
.catch(e => res.send(e))
});
}
With the code in the second snippet in your question, your try/catch block will never do anything useful, because any problem will trigger the .catch method, and because the Promise isn't being awaited. Better not to mix the two styles - either pick await and try/catchor .then and .catch, but not both, else the control flow may become difficult to make sense of.
First of all, async-await can be used in functions which returns a promise.
Why we have to use await instead of then. await process the code asynchronously by making the execution feel like synchronous.
In your first example, everything works fine as expected. But in the second one, you need to await the findOne query. Here the fineOne will work asynchronously
exports.query = async(req, res) => {
try{
user.findOne({})
.then(async(result) =>{
let orderResult = await order.findOne({id: result.id})
.catch(e => res.send(e)
} catch(error) {
res.send(error)
}
}
which can be again simplified to
exports.query = async(req, res) => {
try{
let result = await user.findOne({});
let orderResult = order.findOne({id: result.id});
} catch(error) {
res.send(error)
}
}

MongoDB: Return query from seperate file

I don't know if it is possible or maybe I don't know how to quite google the question properly but I am wondering is their a way to query the MongoDB from one file and return the results to another using node. Lets say I have two files routes.js and helpers.js
routes.js
const finder = require('../db_helpers/userHelpers');
exports.getIndex = (req, res, next) => {
finder.findUser()
.then(user => {
if (!user) {
return res.redirect('/signup')
}
res.render('shop/landing', {
pageTitle: 'landing'
});
})
.catch(err => {
console.log(err)
})
};
helpers.js
const User = require('../models/user');
exports.findUser = (user) => {
User.findOne()
.then(user => {
console.log(user);
return user
})
.catch(err => {
return err
})
};
This is what I have been working with for a few hrs now changing things around and such but to no avail. Like I said I may have been googling wrong but if someone could point me in the right direction or tell me this isn't possible that would be greatly appreciated.
The problem is that you are expecting a promise with finder.findUser().then() in the routes.js file but not returning a promise in userHelpers.js, so the then statement never invoked since the promise is never met.
Your userHelpers.js file should look like:
const User = require('../models/user');
exports.findUser = user => {
User.findOne((err, user) => {
if (err) return Promise.reject(err);
return Promise.resolve(user);
});
};

getting value from a chained promises

im really new to this of promises and im getting headaches trying to understand this, so now im trying to get an answer from a method that returns a promise, then i catch the value in a conditional and make some other operations
let addService = async(req, res) => {
checkCategoryExists(param).then(result => {
if(result){
// code here
}
}).catch(err => console.log(err));
}
let checkCategoryExists = async(param) => {
let docs = db.collection(collectionName).doc(param);
docs.get()
.then(categoryDoc => {
if(categoryDoc.exists){
if(categoryDoc.data().param== param){
return true;
}
} else {
return false;
}
})
.catch(err => false);
}
the method "checkCategoryExists" is a query to a firestore db. When i tried to check if result variable is true or false, it happens to be undefined. its not with ".then()" that i get to catch the value from the returned promise? if someone can help me, thanks in advance
So as mentioned above I think your issue is based on not returning the results of your document search so both examples below handle that.
I also noticed that you were using async tags on your functions but not ever using await so I wanted to give examples of both ways of doing it in case you wanted to use Async/Await but weren't certain how.
In the promise chain example I'm relying on the syntax of arrow functions to create the returns (no {} means return right side of equation) since none of your given code requires data manipulation before return (if your actual code needs that you should of course use brackets and remember your return statement :D )
If you choose to use Async/Await you can structure the code much more closely to synchronous examples and use try catch statements. Sometimes I find this syntax more clear if I have to do a lot of processing/manipulation before returning a result.
Good Luck :)!
// Promise Chains
const addService = (req, res) =>
checkCategoryExists(param)
.then(result => /* do stuff */)
.catch(err => console.error(err.message));
const checkCategoryExists = param =>
db.collection(collectionName.doc(param)
.get()
.then(categoryDoc =>
Promise.resolve((categoryDoc.exists && categoryDoc.data().param === param))
);
// OR using Async/Await
const addService async (req, res) => {
try {
const catExists = await checkCategoryExists(param);
if (!catExists) {
throw new Error('error code');
}
// Do Stuff.
} catch (error) {
console.error(error);
}
};
const checkCategoryExists = async param => {
try {
const docs = await db.collection(collectionName)
.doc(param)
.get();
return (docs.exists && docs.data().param === param);
} catch (error) {
console.error(error)
}
}

Categories

Resources