3

I am new to NodeJS(Express). I am trying to build an API that will list the "Rooms" of a Hotel, and "Photos" for each room.

What I am trying to do is: - get a list of all rooms by a hotel - loop through this list of rooms and add photos - return JSON with rooms and photos

My problem is that I can not seem to attach the "photos" result to the "rooms result.

async.waterfall([
 // Function 1
 function(callback) {
 // Find all Hotel Rooms
 Room.find({ 'hotel': req.params.hotel_id})
 .exec(function(err, rooms) { 
 callback(null, rooms); // Send rooms to function 2
 });
 },
 // Function 2
 function(rooms, callback) { 
 // Loop through each room to get photos for each room
 rooms.forEach(function(room) { 
 // Get photos for room
 RoomPhoto.find({ 'room': room._id}) 
 .exec(function(err, photos) { 
 // I want to add these "photos" to the main "rooms" object, but attached to each individual "room"
 room.photos = JSON.stringify(photos);
 });
 })
 console.log(rooms)
 callback(null, rooms);
 }
], function(err, results) {
 if(err) console.log('err');
 res.json(results);
});

Any help would be greatly appreciated, as I have been hours trying to figure it out.

Thanks

Ray


I have updated my script to this, but the "photos" are still not going into the final "rooms" object. I have included the code and also the console output.

Code:

exports.index = function(req, res, next) { 
 Room.find({ 'hotel': req.params.hotel_id})
 .exec(function(err, rooms) {
 async.each(rooms, function(room, room_cb) {
 console.log('Room Name: ' + room.name);
 RoomPhoto.find({ 'room': room._id})
 .exec(function(err, photos) {
 console.log('Photos: ' + photos);
 room.photos = photos; 
 // photos should now be in the main "rooms" object right??
 // I can console.log the photos here to prove they exists
 room_cb();
 });
 }, function(err) {
 if(err) {
 console.log('Error: ' + err)
 }
 else {
 // But the photos are not in the final "rooms" object
 console.log('FINAL ROOMS OBJECT, why are the photos not here??');
 console.log(rooms);
 res.json(rooms);
 }
 }) 
 });
};

Here is the console output for the above script:

Room Name: Test Room 1

Room Name: Test Room 2

Photos:

{ _id: 5acd3094e0026f38ca4b44bc,
 src: 'https://picsum.photos/200/300/?image=252',
 room: 5acd3094e0026f38ca4b44bb,
 __v: 0 }

Photos:

{ _id: 5acd30c8cec87777eefcd32d,
 src: 'https://picsum.photos/200/300/?image=252',
 room: 5acd30c8cec87777eefcd32c,
 __v: 0 }

FINAL ROOMS OBJECT

[ { _id: 5acd3094e0026f38ca4b44bb,
 name: 'Test Room 1',
 hotel: 5accd4e1734d1d55c3195c84,
 __v: 0 },
 { _id: 5acd30c8cec87777eefcd32c,
 name: 'Test Room 2',
 hotel: 5accd4e1734d1d55c3195c84,
 __v: 0 } ]
Muhammad Faizan
1,7791 gold badge15 silver badges37 bronze badges
asked Apr 11, 2018 at 9:27

4 Answers 4

4

Since the function RoomPhoto is also asynchronous, then you need to wait until all the photos are loaded before calling the callback function. Like that:

// Function 2
function(rooms, callback) { 
 // https://caolan.github.io/async/docs.html#each
 // Loop through each room to get photos for each room
 async.each(rooms, function(room, room_cb) { 
 // Get photos for room
 RoomPhoto.find({ 'room': room._id}) 
 .exec(function(err, photos) { 
 room.photos = JSON.stringify(photos)
 room_cb()
 });
 }, function (err) {
 console.log(rooms)
 callback(null, rooms)
 })
}
answered Apr 11, 2018 at 9:51
Sign up to request clarification or add additional context in comments.

5 Comments

It makes sense what you say, but when I run the script, the "photos" are still not attached to the final "rooms" object
@RayPhelan Try async.eachOf - caolan.github.io/async/docs.html#eachOf - rooms[key].photos = photos...
i just tried async.eachOf... but still the photos have not being added to the "rooms" object when I output the final function
I think that this is not a problem with processing asynchronous operations. Try to find the problem in the requests to the MongoDB...
It does seem to be an issue with MongoDB rather than Async. For some reason, the mongodb "_id" is causing a problem, because in order to add the "photos" to "room", then i needed to JSON.parse(rooms) first, but the _id caused an error. So I used this: rooms = JSON.parse(JSON.stringify(rooms).replace(/_id/g,"room_id"));
0

I found something that works... For some reason, the MongoDB "_id" was causing a problem.

exports.index = function(req, res, next) {

Room.find({ 'hotel': req.params.hotel_id})
.exec(function(err, rooms) {
 rooms = JSON.parse(JSON.stringify(rooms).replace(/_id/g,"room_id")); 
 console.log(rooms);
 console.log('---------------------------');
 async.each(rooms, function(room, room_cb) { 
 RoomPhoto.find({ 'room': room.room_id})
 .exec(function(err, photos) {
 console.log('Room: ' + room.id);
 console.log('Photos: ' + photos);
 room.photos = photos;
 //console.log('Photos: ' + rooms[key].photos);
 room_cb(); 
 });
 }, function(err) {
 if(err) {
 console.log('Error: ' + err)
 }
 else {
 console.log('FINAL ROOMS OBJECT');
 rooms = JSON.parse(JSON.stringify(rooms).replace(/room_id/g,"_id")); 
 console.log(rooms);
 res.json(rooms);
 }
 }) 
});

};

answered Apr 11, 2018 at 23:16

Comments

0

Since you are new to node.js you may not be aware of the latest standards, and things you can use.. Here's how you can accomplish it without including async module at all...

const requestHandler = async (req, res, next) => {
 let rooms = await Room.find({ 'hotel': req.params.hotel_id})
 .exec()
 .catch(err => { 
 console.log(err);
 throw err;
 });
 // Get photos for room
 let photos = await Promise.all(rooms.map(
 room => RoomPhoto.find({ 'room': room._id})
 .catch(err => { 
 console.log(err);
 throw err;
 });
 ))
 rooms = rooms.map((room, index) => {
 room = room.toObject();
 room.photos = JSON.stringify(photos[index]);
 return room;
 });
// Now your "rooms" array will have room objects, with photos populated.
 res.send(rooms);
});

To export it you can do it like this:

module.exports.index = requestHandler;
answered Apr 11, 2018 at 10:03

7 Comments

I tried copying this code in, but it didnt work for me
did it throw any error? And did you copy it as whole?
can you mention your nodejs version and expressjs version
This is the error message, and page just waits without doing anything: (node:15404) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot set property 'photos' of undefined (node:15404) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Node v8.9.4, express 4.16.0
|
-1

Try placing your second callback(null, rooms); line (the one near the end) under the room.photos = JSON.stringify(photos); line.

Because of the asynchronous nature of JavaScript, it is probably running before your query has even finished getting the photos.

You might also want to add some checks in case there are no photos for a room.

answered Apr 11, 2018 at 9:42

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.