2

This is an example of what I am trying to do, retrieve all the post inside a mongo db, for each of the post populate the author then use author object to retrieve the profile picture from Cloudinary.

What is the correct way to go about doing this? I've tried numerous ways of populating arrays and sending those in the response but because of the async call they are never run before the res is sent.

router.get('/posts',auth, function(req, res, next) { //var id = req.payload._id; Post.find(function(err, posts){ if(err){ return next(err); } posts.forEach(function(post){ post.populate('author',function(err,post){ post.image = cloudinary.image("v"+post.author.avatarVersion+"/profile/"+post.author._id,{ width:100, height:100,crop:'thumb',gravity:'face',radius:'max' }) //here the post object is updated console.log(post) }) }) //res.json(some posts array); }); }); 

Solution thanks to Dan Moldovan!

router.get('/posts',auth, function(req, res, next) { var id = req.payload._id; Post.find({}).populate('author').exec(function(err,posts){ if(err){ return next(err); } var updatedPosts = []; posts.forEach(function(post){ post.image = cloudinary.image("v"+post.author.avatarVersion+"/profile/"+post.author._id,{ width:100, height:100,crop:'thumb',gravity:'face',radius:'max' }) updatedPosts.push(post); }) res.json(updatedPosts); }) 

2 Answers 2

1

You can just chain the populate query to the first one and have a final callback afterwards such as

Post.find({}).populate('author').exec(function (err, posts) { if(err){ return next(err); } posts.forEach(function(post){ // here each post will already have author appended }); }); 
Sign up to request clarification or add additional context in comments.

2 Comments

Hey Dan, thanks for the reply! That works to make populating more efficient. However I still cannot put the updated posts array in the res.json() .
forEach is sychronous so you should be able to res.json the array successfuly immediately after the forEach
1

Dan's solution is correct, but I want to explain the problem you're encountering. Because post.populate() is a database call, it means the code is asynchronous. This means the next post in the forEach() will start running before the .populate() is finished with the previous post. This means that not all posts will be finished before res.json() executes. A solution (which is not needed in this case, but can be used by your current code) is to use the async library:

var async = require("async"); router.get('/posts',auth, function(req, res, next) { var id = req.payload._id; Post.find(function (err, posts) { if (err) { return next(err); } // This is a forEach that waits until all posts are populated before moving on async.each(posts, function (currentPost, postCallback) { currentPost.populate("author", function (err, post) { if (err) { return postCallback(err); } post.image = cloudinary.image("v" + post.author.avatarVersion + "/profile/" + post.author._id, { width: 100, height: 100, crop: 'thumb', gravity: 'face', radius: 'max' }); // the callback function for the current post postCallback(); }); }, function (error) { // the final callback function once all postCallback()'s have been executed, or an error if (error) { return next(error); } // here, everything is finished res.json(posts); }); }); }); 

Again, Dan's solution is correct so don't use this code. Just keep it in mind when you encounter issues like this.

1 Comment

Thanks! I'm certain this will come in handy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.