0

EDIT: I found out that "async" could be added to the function signature, and while the output does go in the proper order, I still get an error that I can't set headers after they are sent, even though I am not setting them anywhere else. I've modified the code to reflect this.

I'm having an issue with my Google Firestore api using ExpressJS. It seems that the result is sent before the Firestore query completes and I'm not sure why, as I'm not doing anything with res otherwise. It seems that Firestore queries are async but I don't know how to have my endpoint wait for the Firestore data before sending results. Here is my router code:

router.post('/some_endpoint/get_something', async function (req, res) {
    console.log("Getting firestore data...")
    let db_data = null;
    let some_val = req.body.some_val;
    let colRef = db.collection("some_collection");
    await colRef.where("some_field", "==", some_val)
        .get()
        .then(function(querySnapshot) {
            querySnapshot.forEach(function(doc) {
                console.log("Still processing...")
                db_data = doc.data()
            })
            res.json({  <---- This is where it breaks
                status: 200,
                data: db_data
            })
        })
        .catch(function(error) {
            console.log("Error getting doc: ", error);
        })

    console.log("We're done!")
});

This is the output order (EDIT with new output order):

Getting firestore data...
Still processing...
Error getting doc: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client...
We're done!
VagrantC
  • 687
  • 2
  • 13
  • 31
  • Does this answer your question? [Error: Can't set headers after they are sent to the client](https://stackoverflow.com/questions/7042340/error-cant-set-headers-after-they-are-sent-to-the-client) – Chris32 May 07 '20 at 13:20

1 Answers1

0

The error message is telling you that you're trying to send multiple responses, which is not valid. Here, your code is calling res.json() many times, once for each document in the query results:

            querySnapshot.forEach(function(doc) {
                console.log("Still processing...")
                res.json({
                    status: 200,
                    data: doc.data()
                })
            })

You should only call res.json() once with the final result to send to the client, after you're done iterating the results.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Sorry I forgot to mention that there is only 1 document returned. I even tried to store the value and do res.json outside of the forEach loop, but I get the same error. Also, "Still processing..." outputs after the "We're done!" output, so even if there were multiple documents, I would at least see 1 "Still processing..." before the "We're done!" – VagrantC May 06 '20 at 19:11
  • "We're done" will always print first, because `get()` is asynchronous and returns immediately. Calling then/catch doesn't pause the code. They are callbacks that get executed some time later, after the query completes. You're probably still doing something wrong if you're not getting the results you expect. – Doug Stevenson May 06 '20 at 19:50
  • Thanks for the help, I just added async/await to the function, and it runs in the proper order, but I still get the same error when I try to add the data to `res` (I edited the question to reflect this) – VagrantC May 06 '20 at 22:40