How to query MongoDB using aysnc/await? - node.js

So I'm following a course on Udemy to learn web development using Node/Express/MongoDB and using mongoose to interact with the database.
I have reached a lecture where the instructor started explaining on how to query the database so here created a mongoose model and named it Tour and in a controller file he wrote this to query the data that we already imported like this:
exports.getAllTours = async (req, res) => {
try {
const queryObj = { ...req.query };
const exludedFields = ["sort", "limit", "page", "fields"];
exludedFields.forEach(el => delete queryObj[el]);
const query = Tour.find(queryObj);
const tours = await query
res.status(200).json({
status: 'success',
data: {
tours: tours
}
})
} catch (err) {
res.status(404).json({
status: 'fail',
message: err.message
});
}
}
As you can see from the code above we used the handler function getAllTours to get all the tours which by the way it handles this route app.get('/api/v1/tours/').
At the beginning of the lecture the code for the query was like this: const query = await Tour.find(queryObj); and then he removed the await keyword to look like the code above were he included the await keyword for the tours object later.
The instructor explained this by saying that if we used the await on the query like this const query = await Tour.find(queryObj); then the code will execute immediately and then we would not be able to chain other methods on the query like .where() or .equals().
I need someone to explain these questions for me:
why would the code execute immediately if we used await?
the Tour.find() does need time to query the database, so I think we should add await, shouldn't we?

The Tour.find() returns a promise (is an async call), adding the await in front of it will wait until the promise is resolved and return it results.
If you want to do a where() after you do the find you can do it after the query is executed since you will have the results in that object in your case the tours object.
Honestly I prefer doing const tours = await Tour.find() you save a varaible that is kinda of innecesary, just is necesary if you want to catch an error of the promise or something like that.
In your case you using a try catch so the error will go to the catch block if something happens with the query (connection issues or other problems)

Related

mongoose Query was already executed

I was trying to learn a MERN stack beginner tutorials and i encountered this problem the query was already executed I'm getting this error whenever .findOne() is executed:
MongooseError: Query was already executed: elders.findOne({ _id: new ObjectId("636caae0e3c24f1df7b4e6b7...
router.put("/updateElders", async (req,res)=>{
const newAge = req.body.newAge
const id = req.body.id
try{
await EldersModel.findById(id, (error, eldertoUpdate) =>{
eldertoUpdate.age = Number (newAge);
eldertoUpdate.save();
})
}catch(err){console.log(err);}
You should decide on whether to use callbacks or promises. You chose to use await (used for awaiting promises) and callbacks (your second parameter passed to findById. You should decide on which one to use, e.g.:
router.put("/updateElders", async (req,res)=>{
const newAge = req.body.newAge
const id = req.body.id
try{
const eldertoUpdate = await EldersModel.findById(id);
eldertoUpdate.age = Number(newAge);
await eldertoUpdate.save();
}catch(err){
console.log(err);
}
this code uses promises returned from findById and save calls and awaits those.

Hi. How to use find query in mongoose - Express + MongoDB base

What I'm doing is trying to write logic for cart in my web-app what I'm creating. Problem what I'm dealing right now is that when I use query to find cart by id :
const isCart = await cartModel.findById("5f82372f2654ce1d18553ac4");
or like this
const isCart = await cartModel.find({ _id : "5f82372f2654ce1d18553ac4"} );
if cart exist with this id all works good it returns me this cart, but if in DB cart doesn't exist with this id then it throws me an error. WHY (pic below) ?? It would make more sense if it returns empty array or object so I can continue building logic of I want to do. But once it throws error it shuts down all further logic. I can't write if there is nothing found, create new cart and so on. Hopefully you got what I mean. Is it something I don't write correctly query or it is the way it is. If so, then I guess I should already control it on client side - if it returns error to a client side then client side sends new request to a new rout on creating a new cart and so on...
Here is solution what I did after someone told me that I might try do with try - catch
Here is a result, it works I'm not sure if this solution is right tho
async function sendCart (code, cart, res){
res.status(code).json({
status: "success",
data: {
cart
}
})
}
exports.createCart = async (req, res, next) => {
try{
const isCart = await cartModel.findById(req.params.id);
await sendCart(200, isCart, res);
}
catch(error){
const newCart = await cartModel.create({ items: [] });
await sendCart(201,newCart, res);
}
}

Google Cloud Function not working properly with firestore .get() - NODE JS

I'm a novice web developer. I'm currently working on Firebase for a blog project.
This is my DB structure:
On my home page, I'm using this code to get the some latest posts from each of my category
app.get('/test', (req, res) => {
async function getDocument(db) {
const ex = await db.collection('Posts').where('category', '==', 'Exercise').orderBy("artID", "asc").limit(4).get();
const hl = await db.collection('Posts').where('category', '==', 'Health').orderBy("artID", "asc").limit(2).get();
const fl = await db.collection('Posts').where('category', '==', 'Food & Lifestyle').orderBy("artID", "asc").limit(3).get();
const md = await db.collection('Posts').where('category', '==', 'Mindfulness').orderBy("artID", "asc").limit(2).get();
// const author = await db.collection('authors').doc(doc.data().author).get();
return {
ex: ex.data(),
hl: hl.data(),
fl: fl.data(),
md: md.data()
}
}
getDocument(db).then(function (data) {
res.send(data);
})
})
I've checked the code, and also made sure that the query is indexed in firestore.
But when I execute the function, the browser is throwing an error:
Error: could not handle the request
and I get this error logged in my console.
TypeError: ex.data is not a function
at getDocument (/workspace/index.js:69:14)
at process._tickCallback (internal/process/next_tick.js:68:7)
To get data from Firestore, placing a '.data()' function is needed.
Can somebody help me with where I'm typing the code wrong
I think the response of await db.collection('Posts').where('category', '==', 'Exercise').orderBy("artID", "asc").limit(4).get(); is a QuerySnapshot - the filter will match more then one value and this is why you cannot access data() because there is more then one object.
You should do something like ex.docs.map(doc => ({ id: doc.id, ...doc.data() }))
As per #stefan-prokop-cz mentioned, you need to map each document snapshot from ex.docs. Also, you might wanna consider using Promise.all() to run these queries in parallel to improve computation time.

How can I force my function to wait promises before moving on?

I read another posts but they were not solving my particular problem.
I'm trying to move all dependent entries from one user to a default one before deleting the user. But even with Promise.all().then() I'm getting an error saying that I still have foreign key constraints preventing me to delete the user (But if i try to delete it manually after that it works fine, because I really moved all the dependencies to the default user).
async delete (req, res){
try {
const {id} = req.body;
// Here I call all the recipes that this user owns
const recipes = await Recipe.find({where: {chef_id: id}});
// Create the Promises for every UPDATE request in the Database
const changeChefPromises = recipes.map( recipe => {
Recipe.update(recipe.id,{chef_id : 1})
});
/* HERE is the problem, I'm trying to update every single recipe
before I delete the User, but my program is trying to delete
the User before finishing the Update process for all dependencies */
await Promise.all(changeChefPromises).then(() => {
Chef.delete(id);
});
return res.redirect(`/admin/chefs`);
} catch (err) {
console.error(err);
}
}
const changeChefPromises = recipes.map( recipe => {
Recipe.update(recipe.id,{chef_id : 1})
});
This is not creating an array of promises which is what you're expecting. You either need to remove the body of the arrow function or explicitly return the promise inside the body.
E.g.
const changeChefPromises = recipes.map(recipe => Recipe.update(recipe.id,{chef_id : 1}));
or
const changeChefPromises = recipes.map( recipe => {
return Recipe.update(recipe.id,{chef_id : 1})
});
Also, it's a bit weird to mix async/await and .then() so it might also be a good idea to change:
await Promise.all(changeChefPromises).then(() => {
Chef.delete(id);
});
to
await Promise.all(changeChefPromises);
await Chef.delete(id);

await/async: compiler/syntax issue with conditionnals

I am using async/await in a web app to query a DB and use results for other queries.
I have this function:
exports.getByUserAndPurposeForService = async (req,res,next) =>{
var serviceID = await Service.getIdByName(req.params.serviceName)
var user_id = await User.getIdByName(req.params.userID)
if(condition){...}
Await/async works great there but whet it gets to this point:
else {
var userName= **await** ID.getUserID(serviceID, req.params.userID)
user_id = **await** User.getIdByName(userName)
}
else{
res.send('Not Allowed')
}
}
})
those two 'await' create an error in the IDE :
'Parsing error : unexpected token ID'
I don't know why, I can use await on functions outside the conditional but not inside.
Thanks a lot for all the help, I found what the problem was :
I was awaiting these promises inside a .exec() function
Like this :
Model.findOne({..}).exec(function(err, result){
var user = await ...
})
You can't apparently mix chaining with async/await
Thanks for the help !
So I fixed it by using await on Model.findOne({..}).exec() and storing result in variable I could use after

Resources