I'm trying to work on a MERN full stack app where the frontend sends an api call to "/createDeckOfCards" to my nodejs backend. The goal is to click on a button to create a new deck of cards, then return the list of created cards. The parameter numOfCards is sent with this call as well.
So on my nodeJS backend, I have the "/createDeckOfCards" endpoint where I use .map() to iteratively create each card and then save to mongoDB like so:
const allCardsArray = [...Array(req.body.numOfCards).keys()]
allCardsArray.map(async (i)=>{
const eachCard = new eachCardModel({
eachCardTitle: String(i)
})
eachCard.save((err, doc) => {
if (err) return res.status(400).json({ errMsg: "Something went wrong" });
else{
CardDeckModel.findOneAndUpdate(
{_id: req.cardDeckCreated._id},
{$push:{allCards: doc}},
function(error, success){
if (error){
console.log(error)
return res.status(400).json({ errMsg: "Something went wrong" });
} else {
console.log("success")
}
}
)
}
});
})
console.log("COMPLETED") //DOES NOT EXECUTE LAST!
//THIS RETURNS BEFORE THE .map() is done
res.status(200).json({
createdCardDeckID: req.cardDeckCreated._id
})
})
After that, I have a second endpoint "/returnAllCardsInDeck" where I pass in the ID of the cardDeck like so:
CardDeckModel.findOne({_id: req.body.createdCardDeckID}).populate({path: 'allCards', options: { sort: "eachCardTitle" } }).exec((err, cardDeck) => {
if (err) return res.status(400).json({ errMsg: "Something went wrong" });
else {
res.status(200).json({
CardDeck: cardDeck
})
}
})
The problem is, CardDeck returns before the allCardsArray.map() is completed. This would be a problem because I want the user to see ALL cards in the deck once the deck is created. But because the "/returnAllCardsInDeck" executes before the "/createDeckOfCards", it returns be an undefined object.
Also, am I doing this right? Esp with regards to the first part ("/createDeckOfCards").
try this, you can't do async call like this with map. There are patterns to solve this issue. Promise.all is one of them.
const allCardsArray = [...Array(req.body.numOfCards).keys()]
await Promise.all(
allCardsArray.map((i)=>{
const eachCard = new eachCardModel({
eachCardTitle: String(i)
})
return eachCard.save()
.then(
() =>
CardDeckModel.findOneAndUpdate({_id: req.cardDeckCreated._id}, {$push:{allCards: doc}})
.then(() => console.log("success"))
.catch((error) => console.log(error) || res.status(400).json({ errMsg: "Something went wrong" });)
).catch(() => res.status(400).json({ errMsg: "Something went wrong" }))
})
)
console.log("COMPLETED") //DOES NOT EXECUTE LAST!
//THIS RETURNS BEFORE THE .map() is done
res.status(200).json({
createdCardDeckID: req.cardDeckCreated._id
})
})
you can use for of with async/await instead of map like this
const allCardsArray = [...Array(req.body.numOfCards).keys()];
for (let i of allCardsArray) {
const eachCard = new eachCardModel({
eachCardTitle: String(i),
});
try {
let doc = await eachCard.save();
await CardDeckModel.findOneAndUpdate(
{ _id: req.cardDeckCreated._id },
{ $push: { allCards: doc } }
);
} catch (error) {
return res.status(400).json({ errMsg: "Something went wrong" });
}
}
console.log("success");
res.status(200).json({
createdCardDeckID: req.cardDeckCreated._id,
});