Error while trying to get realtime data from firestore database code: 'ERR_HTTP_HEADERS_SENT'

i am trying to build an app and i am very new to all this. So I've built a very simple function to get data from firestore and it works fine from a moment. This is the code:

 async getData(req, res) {
            const dataRef = db.collection(`${req.body.banco}`)
            let result = []
            dataRef.onSnapshot(docSnapshot => {
                docSnapshot.forEach(doc => {
                    const data = doc.data()
                    result.push(data)
                })
                console.log(result)
                return res.status(200).send(result)
            
            }, (error) => {
                console.log(`Erro encontrado: ${error}`)
            })
    }

My problem is that when I try to update any field from the document. It gets updated but I end up receiving this error:

node:_http_outgoing:576
    throw new ERR_HTTP_HEADERS_SENT('set');
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:372:5)
    at ServerResponse.setHeader (node:_http_outgoing:576:11)
    at ServerResponse.header (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:794:10)
    at ServerResponse.send (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:174:12)
    at ServerResponse.json (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:278:15)
    at ServerResponse.send (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:162:21)
    at C:\Users\paulo\Desktop\sirius-back\functions\src\controller\createDocs.js:70:40
    at QueryWatch.onNext (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\@google-cloud\firestore\build\src\reference.js:1914:13)
    at QueryWatch.pushSnapshot (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\@google-cloud\firestore\build\src\watch.js:469:18)
    at QueryWatch.onData (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\@google-cloud\firestore\build\src\watch.js:353:26) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

the app crashes and i have to start it over.

What is the reason of this? How can i get realtime updates from firestore databse after I update something from?

Upvotes: 0

Views: 86

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 599866

In Express you handle each requests by sending a single response to it. Since res a a response object, you can only send a response to the caller once. But since you're using onSnapshot, your code gets called for every change to the data too.

So initially, you load the data and send a response to the caller and all is 👍
But then when an update is made to the database, your code executes again and tries to send another response to the caller - and this is when you get an error.

The solution is to read the data only one with something like:

async getData(req, res) {
    const dataRef = db.collection(`${req.body.banco}`)
    let result = []
    dataRef.get().then((docSnapshot) => {
        docSnapshot.forEach((doc) => {
            const data = doc.data()
            result.push(data)
        })
        console.log(result)
        return res.status(200).send(result)
    }, (error) => {
        console.log(`Erro encontrado: ${error}`)
    })
}

Or a bit simplified:

async getData(req, res) {
    const dataRef = db.collection(`${req.body.banco}`)
    dataRef.get().then((docSnapshot) => {
        const result = docSnapshot.docs.map((doc) => doc.data());
        return res.status(200).send(result)
    }, (error) => {
        console.log(`Erro encontrado: ${error}`)
    })
}

Once you've called res.send(...) the request is complete and the client stops listening. Sending further updates to a response is not possible with an Express request/response model like you're using.

Also see:

You'll need to choose an infrastructure that allows a client to keep listening. Heads up: building something like that is quite involved, and you're likely better off if you use the client-side Firestore SDK to implement such realtime listeners

Upvotes: 1

Related Questions