0

This is my code in the routes file

router.get('/api/', async function(request, response){

    let entries = await Entries.find({}, function(error){
        if(error) console.log(error);
    });

    let catArray = [];
    
    entries.forEach(entry=>{
            catArray.push(entry.category.id);
        });

    console.log(new Set(catArray));

    let categories = [...new Set(catArray)];
    
    categories.forEach(category=>{    

        Entries.find({"category.id":category},function(error,result){
            if(error){
                console.log(error);
            }else{
                response.send(result);
            }
        })
    });
})

In the above code the API returns the JSON result as per the request but there's an error in the server console.. so I am not sure why or how to resolve it. This is the error


events.js:291
      throw er; // Unhandled 'error' event
      ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:530:11)
    at ServerResponse.header (/Applications/MAMP/htdocs/xxxx-server/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/Applications/MAMP/htdocs/xxxx-server/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Applications/MAMP/htdocs/xxxx-server/node_modules/express/lib/response.js:267:15)
    at ServerResponse.send (/Applications/MAMP/htdocs/xxxx-server/node_modules/express/lib/response.js:158:21)
    at /Applications/MAMP/htdocs/xxxx-server/routes/entries.js:144:26
    at /Applications/MAMP/htdocs/xxxx-server/node_modules/mongoose/lib/model.js:5065:18
    at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted 'error' event on Function instance at:
    at /Applications/MAMP/htdocs/xxxx-server/node_modules/mongoose/lib/model.js:5067:15
    at processTicksAndRejections (internal/process/task_queues.js:79:11) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

Based on my this link on stackOverflow,

Error: Can't set headers after they are sent to the client

I understand that I am sending a response multiple times even though I seem to be doing it just once..

Question #1 If there's an error in the console how is the response being sent? (I haven't tried this on the browser.. it's on Postman that I am able to view the result)

Question #2 How do I resolve the error in the console?

Question #3 How do I know which line of code is doing this (Sending response without me doing it)?

Would appreciate some help me on fixing this and helping me understand how to avoid this for other functions.

Alim Bolar
  • 479
  • 4
  • 17

1 Answers1

1

Inside a .forEach() loop, you're calling response.send(). That means you're trying to send multiple responses to a single request. You can't do that and that's what causes the error message about headers have already been sent.

Here's the offending code:

categories.forEach(category=>{    

    Entries.find({"category.id":category},function(error,result){
        if(error){
            console.log(error);
        }else{
            response.send(result);
        }
    })
});

For each category, you are calling response.send(). If there's more than one category, then you're trying to send multiple responses which is an error.

If you want to send multiple results, then collect all the results into an array and then send the array once when you're done collecting all the results.

For example to send an array of results, you can use the Promise interface on your database and track when all the queries are done and then send one combined response:

let categories = [...new Set(catArray)];

Promise.all(categories.map(category => {
    return Entries.find({"category.id":category});
})).then(results => {
    response.send(results);
}).catch(err => {
    console.log(err);
    response.sendStatus(500);
});

Question #1 If there's an error in the console how is the response being sent? (I haven't tried this on the browser.. it's on Postman that I am able to view the result)

The first response is sent and the error occurs when your server tries to send a second response to the same request.

Question #2 How do I resolve the error in the console?

See above. You fix the code so you only send one response.

Question #3 How do I know which line of code is doing this (Sending response without me doing it)?

This particular error and stack trace does not make it obvious. You can see ServerResponse.send in the stack trace which is one clue. Beyond that, you pretty much need to know that the error about "headers already sent" occurs because some code is trying to send headers again after they've already been sent and the most common way that happens is when someone tries to send another whole response to the same request. It would be awesome if Express modified this error message to indicate one likely cause is sending multiple responses to the same request.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • That was perfect!.. just what I needed.. a real example to understand Promises and map and how to use them..:-).. just one quick query if you could help.. as `Entries.find({"category.id":category})` returns an array, I am getting an array of arrays while I would really like to have an array of objects.. so as map returns an array, I think I'd have to handle the object part in the `Entries.find` query which again returns an array.. I'll try see if `findOne` would work.. but if you have some suggestions on the best way to do this, I'd really appreciate it.. – Alim Bolar Jul 21 '21 at 05:17
  • So `findOne` is not really the right way to do it as I tried it and realised what I was doing wrong.. `find` is what is needed.. in this case, is there a way that what I return is actually an object of arrays?.. but `map` would always return an array.. right? What can I do to get the array created by map to be converted into an object wrapper? – Alim Bolar Jul 21 '21 at 05:25
  • @AlimBolar - Well, it's just code to convert an array of objects to something else. I'm really not sure what exactly you want the output to be. I wrote this answer to explain the specific error you asked about and what causes it. – jfriend00 Jul 21 '21 at 05:30
  • Got it..:-).. thanks.. will not confuse you with other non related queries on the same post.. will follow the rules of Stackoverflow.. :-).. – Alim Bolar Jul 21 '21 at 05:37
  • This was the other thing I was trying to understand..:-).. https://stackoverflow.com/questions/46967611/converting-array-of-arrays-into-object .. thanks again for your help.. saved me a lot of time and helped me understand promise and map a lot better.. – Alim Bolar Jul 21 '21 at 06:21
  • @AlimBolar - Cool, glad you got it figured out. – jfriend00 Jul 21 '21 at 06:23