nodejs response returning null array.
how to return a response after map function done
var follow = [];
User.findOne(
{ _id: id },
).then(user => {
user.following.map(results => {
User.find({ _id: results.user })
.exec()
.then(res => {
follow.push(res);
});
});
res.json({
status: true,
follow // returning null array
});
});
};
You need to collect the promises and use Promise.all() to know when they are all done:
User.findOne({ _id: id }).then(user => {
let promises = user.following.map(results => {
return User.find({ _id: results.user })
.exec()
.then(res => {
return res;
});
});
Promise.all(promises).then(follow => {
res.json({
status: true,
follow // returning null array
});
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
Note, there is no reason in your original code to use .map() if you weren't going to return anything from the .map() callback. In my code, I return the promise.
Related
I am working on a MERN stack project. I saw that some functions in server side related to fetching or saving data in mongodb have lot of callbacks. In order to avoid callbacks hell, now I am trying promises based solution. However I have encountered issue with this promised based solution. I have a condition and depending on its value I want to either proceed further or just return response and stop.
const mId = req.body.mId;
const cId = req.body.cId;
const lId = req.body.lId;
LP.findOne({
'userId': lId,
'courseId': cId
})
.then( lP => {
return lP.materials;
})
.then( materials => {
if( materials.includes(mId) ) {
console.log('A')
return res.json({'status': 'success'})
}
else {
materials.push(materialId)
return LP.findOneAndUpdate({
'userId': learnerId,
'courseId': courseId
}, {
$set: { materials: materials}
}, {
new: true
})
}
})
.then( update => {
console.log('B')
return res.json({ 'status': 'success' })
})
.catch( err => {
return res.json({ 'status': 'fail' })
})
In above code after A is printed B is also printed and further code is executed which gives me: Cannot set headers after they are sent to the client error. I think that is how promises are supposed to work. But what are the possible solution to avoid this problem. How to return res early on and do not execute code further.
Thanks
Just dont return to stop the chain:
const mId = req.body.mId;
const cId = req.body.cId;
const lId = req.body.lId;
LP.findOne({
'userId': lId,
'courseId': cId
})
.then( lP => {
return lP.materials;
})
.then( materials => {
if( materials.includes(mId) ) {
console.log('A')
res.json({'status': 'success'})
}
else {
materials.push(materialId)
return LP.findOneAndUpdate({
'userId': learnerId,
'courseId': courseId
}, {
$set: { materials: materials}
}, {
new: true
})
}
})
.then( update => {
console.log('B')
res.json({ 'status': 'success' })
})
.catch( err => {
res.json({ 'status': 'fail' })
})
I have a method that deletes products and before it does it check if the user who is trying to delete the product is the user who created it. When i execute it with Insomnia it successfully removes the product but i get an error on the console saying cannot set headers after they are sent to the client.
My method:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, () => {
return res.status(401).json("Not authorized");
})
.then(() => {
return res.status(200).json("Product deleted");
})
.catch((err) => {
return res.status(500).json({
error: err,
});
});
};
I'm pretty sure this is happening because I'm chaining a .then() and .catch() after executing it.
I tried to do this but it didn't work because the err parameter that I'm sending to the callback function is null.:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, (err) => {
if (err) {
return res.status(401).json("Not authorized");
}
return res.status(200).json("Product deleted");
});
};
When i tried this second approach I always got the 200 status, meanwhile the product didn't delete.
Any idea how to deal with this?
You can try something like this:
Product.deleteOne({ _id: id, userId: req.user._id }, (err, result) => {
if(err) {
return "something"
}
return "something else"
});
or: in async / await way
try {
await Product.deleteOne({ _id: id, userId: req.user._id });
} catch (err) {
// handle error here
}
By the way, why you are passing userId at the deleteOne method?
I believe it may just be an error in my logic, but i'm not sure where the problem is exactly and I am looking for help debugging. I am using Firebase Cloud Firestore. I am trying to query through my "follows" collection to return all data where the key "senderHandle" is equal to the params handle and then push that data to the empty array followData. Then, loop through each followData and make a document query call to the "posts" collection. Then, loop through each of the returned documents from the "posts" collection and push each data to the empty array "posts".
When I try to return the posts array though it returns empty. My overall goal is to get all of the users the params handle follows, loop through each user to get their posts, and then push their posts into an empty array.
Functions code:
// fetch home-specific posts (of users following)
exports.getHomePosts = (req, res) => {
let posts = [];
let followData = [];
// get following users
const followDocument = db
.collection("follows")
.where("senderHandle", "==", req.params.handle);
followDocument
.get()
.then((data) => {
if (data.query.size == 0) {
return res.status(400).json({ error: "Not following any users" });
} else {
data.forEach((doc) => {
followData.push(doc.data());
});
}
})
.then(() => {
followData.forEach((follow) => {
db.collection("posts")
.where("userHandle", "==", follow.receiverHandle)
.where("location", "==", "explore")
.get()
.then((data) => {
data.forEach((doc) => {
posts.push({
postId: doc.id,
body: doc.data().body,
userHandle: doc.data().userHandle,
createdAt: doc.data().createdAt,
commentCount: doc.data().commentCount,
likeCount: doc.data().likeCount,
userImage: doc.data().userImage,
});
});
});
});
return res.json(posts);
})
.catch((err) => {
res.status(500).json({ error: err.message });
});
};
followData returned:
[
{
"receiverHandle": "John Doe",
"senderHandle": "bear"
},
{
"senderHandle": "bear",
"receiverHandle": "Yikies"
},
{
"receiverHandle": "bear",
"senderHandle": "bear"
},
{
"receiverHandle": "anon",
"senderHandle": "bear"
},
{
"senderHandle": "bear",
"receiverHandle": "BigYikes"
}
]
There are multiple issues with this code.
You are not waiting promise to be resolved.
Multiple returns statements
You would not create global arrays like posts and followData[you can return in then for next callback]
Code:
// fetch home-specific posts (of users following)
exports.getHomePosts = (req, res) => {
const followDocument = db
.collection("follows")
.where("senderHandle", "==", req.params.handle);
followDocument
.get()
.then((data) => {
if (data.query.size == 0) {
throw new Error("NOT_FOUND");
} else {
return data.map((doc) => doc.data());
}
})
.then((followData) => {
const promises = followData.map((follow) => {
return db
.collection("posts")
.where("userHandle", "==", follow.receiverHandle)
.where("location", "==", "explore")
.get();
});
Promise.all(promises).then((results) => {
const posts = results
.map((data) => {
return data.map((doc) => ({
postId: doc.id,
body: doc.data().body,
userHandle: doc.data().userHandle,
createdAt: doc.data().createdAt,
commentCount: doc.data().commentCount,
likeCount: doc.data().likeCount,
userImage: doc.data().userImage,
}));
})
.flat();
return res.json(posts);
});
})
.catch((err) => {
if (err.message === "NOT_FOUND") {
return res.status(400).json({ error: "Not following any users" });
}
res.status(500).json({ error: err.message });
});
};
I am facing the issue while fetching the latest data from MongoDB. every 3 hours I am pushing the data to the MongoDB when I fetch the latest data's I am facing an issue.
Here is the Schema
var abc = new Schema({
item_name: String,
uploadedDate: String, //"6-29-2019"
date : Date
});
Fetch the latest data's
req.body.uploadedDate = "7-2-2019" String
router.post('/todayList', (req, res, next) => {
abc.find({ "uploadedDate": { "$eq": req.body.uploadedDate} })
.then(product => {
let final = funct.duplicate(product, 'item_name'); here i am filter duplicate object
var result = [];
final.forEach(comp => {
abc.find({item_name": comp.item_name, "uploadedDate": { "$eq":
req.body.uploadedDate} }) // here i am fetching the latest uploaded data based on the item_name and pushing to the 'result'
.sort({"date":-1})
.limit(1)
.exec((err, docs) => {
console.log(docs); //i am getting the latest data here
result.push(docs);
});
})
//but here the value of 'result' is empty array
res.status(200).json({
data: result
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
I am not able to find why it's giving an empty array. please help me with this
The code inside the for loop is asynchronous.
Therefore, the response is sent with an empty result, before the queries in the for loop are done running.
You should make the code wait for the queries to finish running before sending the response.
router.post('/todayList', (req, res, next) => {
abc
.find({ uploadedDate: { $eq: req.body.uploadedDate } })
.then(product => {
let final = funct.duplicate(product, 'item_name')
var promises = []
final.forEach((err, documents) => {
promises.push(
new Promise((resolve, reject) => {
if (err) return reject(err)
abc
.find({ item_name: comp.item_name, uploadedDate: { $eq: req.body.uploadedDate } })
.sort({ date: -1 })
.limit(1)
.exec((err, docs) => {
if (err) return reject(err)
resolve(docs)
})
})
)
})
Promise.all(promises).then(result => {
res.status(200).json({
data: result
})
})
})
.catch(err => {
console.log(err)
res.status(500).json({
error: err
})
})
})
How would I structure a function that has multiple Mongoose.findOne() nested in each other?
I need to do something like
const userId = '...';
const postId = '...';
const imageId = '...';
User.findById(userId).then(user => {
if (!user) {
return res.status(400).json({
status: 'error',
err: 'User not found',
});
}
Post.findById(postId).then(post => {
if (!post) {
return res.status(400).json({
status: 'error',
err: 'Post not found',
});
}
Image.findById(imageId).then(image => {
if (!image) {
return res.status(400).json({
status: 'error',
err: 'Image not found',
});
// DO SOMETHING WITH VARIABLES 'user', 'post', AND 'image'
}).catch(err => { .. });
}).catch(err => { .. });
}).catch(err => { .. });
Since Collection.findById() returns a promise, I guess I should use chaining instead of this structure.
So it might be something like
User
.findById(userId)
.then(user => Post.findById(postId))
.then(post => Image.findById(imageId))
.then(image => {
// DO SOMETHING WITH VARIABLES 'user', 'post', AND 'image'
});
.catch(err => { .. });
but I don't know how to access the variables user, post, and image, and how to throw the errors, so I can access them in my catch statement.
Edit
I have tried this
async function getPostAsync() {
const userId = '597989c668189f31483ffdbf';
const postId = '597989c62624ea74750c74f8';
if (!userId) {
throw new Error('User id missing');
}
if (!postId) {
throw new Error('Post id missing');
}
const user = await User.findById(userId);
const post = await Post.findById(postId);
return post;
}
app.get('/', (req, res) => {
getPostAsync().then(post => {
res.json({
status: 'success',
});
}).catch(err => {
res.status(400).json({
status: 'error',
err
});
})
});
but I just receive
{
"status": "error",
"err": {}
}
Am I doing something wrong?
But I get the same result even with
async function getPostAsync() {
throw new Error('msg');
return Post.find();
}
so I might be calling the async function wrong.
You can use Promise.all:
Promise.all([
User.findById(userId),
Post.findById(postId),
Image.findById(imageId)
])
.then(result)=>{
let user = result[0];
let post = result[1];
let image = result[2];
})
.catch(err => { .. });
Or with destructing assignment:
Promise.all([
User.findById(userId),
Post.findById(postId),
Image.findById(imageId)
])
.then(([user, post, image])=>{...})
.catch(err => { .. });
You can't access those variables inside a later promise's then, but you can get round it by assigning the local resolved values to global variables
let globalUser, globalPost; // create variables for later
User
.findById(userId)
.then(user => {
globalUser = user; // assign to global
return Post.findById(postId)
})
.then(post => {
globalPost = post; // assign to global
return Image.findById(imageId)
})
.then(image => {
// DO SOMETHING WITH VARIABLES 'globalUser', 'globalPost', AND 'image'
})
.catch(err => {... });
EDIT: or when using async/await:
async function() {
const user = await User.findById(userId);
const post = await Post.findById(postId);
const image = await Image.findById(imageId);
// do something with user, post and image
}
Seeing as your promises don't rely on each other you could also use Promise.all() in an async function:
async function() {
const result = await Promise.all([
User.findById(userId),
Post.findById(postId),
Image.findById(imageId)
]);
const [user, post, image] = result;
// do something with user, post and image
}
EDIT 2: Error handling
async function getImage() {
let user;
try {
user = await User.findById(userId);
} catch (error) { // deal with rejection of `User.findById`
// do something with error
}
// if these fail the entire function will throw
const post = await Post.findById(postId);
const image = await Image.findById(imageId);
return image;
}
getImage()
.then(image => {... })
.catch(error => {... }); // deal with rejection of `getImage` as a whole
The above code showcases the ways you can handle errors in an async function. The first is how we deal with an error in the User.findById function, by simply wrapping it in a try catch block.
The second method is by simply letting the entire async function throw an error. I.e. if the Post.findById or Image.findById promises reject, the entire getImage() promise will reject, which you can deal with in the .catch() handler.