0

I'm trying to display a list of comments on my react page. For this I have setup a NodeJS server which loads the data from Firebase and passes it on to React. I am able to get it to load the comments list and display them, but when I try to add a comment, the server crashes with the following error:

@firebase/database: FIREBASE WARNING: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

This is because I am using:

firebase.database().ref('my-path').on("value", ...)

However, if I use firebase.database().ref('my-path').once("value", ...) then I lose the ability to update the comments as soon as a new comment is posted. Is there a way to be able to have a listener attached to the database and still be able to update the contents of that database?

Here is my NodeJS code:

app.get("/comments/:id", (req, res) => {
    const itemsRef = firebase.database().ref(`comments/${req.params.id}`);
    itemsRef.on('value', (snapshot) => {
        let comments = snapshot.val();
        return res.status(200).json(comments);
    })
})

app.post("/comments/:id", (req, res) => {
    const itemsRef = firebase.database().ref(`comments/${req.params.id}`);
    itemsRef.push(req.body);
})

The error occurs after the post request is called.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I don't think you ever want to use `on()` like that in a route handler on the backend. It's more typical to use `once()` to fetch data a single time, which automatically removes the listener after data is available. – Doug Stevenson Sep 24 '19 at 03:43
  • Then how would you recommend that I make it live data-esque? Would I have to hardcode a page refresh in react, so at the end of the post request add a res.send() or res.status() which notifies react that the request is complete, and then inside react have a function which manually re-renders the page? Or would this be something more like where I get the new comments list in the post request and send it back to react and tell it to render the comments list sent from the post request response? – MustachedNinja Sep 24 '19 at 04:31
  • 1
    Usually, the client just requests the data directly from the database using the client SDKs provided by Firebase. Going through some nodejs middleware doesn't really help the ability to make "live" looking data. – Doug Stevenson Sep 24 '19 at 04:34
  • @DougStevenson I'd consider that combination of comments a valid answer. – Frank van Puffelen Sep 24 '19 at 07:24

1 Answers1

0

You're sending a response back to the client with:

res.status(200).json(comments)

This sets at least two headers (the status, and the response type) and then sends the response. Next time you get an update from the database, this code runs again, and again tries to send the two headers. But in HTTP all headers must be before the main body of the response. So the second time this code runs, it throws an error.

If you want to keep sending more data to the client, you'll need to use more primitive methods of the response object to prevent sending headers, or other illegal data. While possible, it's more complex than you may think, as the client needs to handle this response stream, which most clients won't.

I'd highly recommend looking at Doug's alternative, which is to just use the Firebase Realtime Database from the client directly. That way you can use the client SDK that it has, which handles this (and many more complexities) behind the scenes.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thats what I originally did, and it worked fine, but since that caused my firebase config file to be placed in my client-side code, I thought that it would be best to move it to Node so that the code was more cleanly separated between display and logic, and primarily so that the user didn't have access to my firebase config file. Would it be possible to have firebase access my database on the client side, but still hide the config file from the user? – MustachedNinja Sep 24 '19 at 17:10
  • Your Firebase config file, you mean the configuration data? If so, see https://stackoverflow.com/questions/37482366/is-it-safe-to-expose-firebase-apikey-to-the-public Trying to build your own little Firebase database is not necessarily going to be more secure. You're essentially replacing the configuration data and security rules with your own URL and code. Both can be secured properly (as many Firebase projects and custom APIs show), and both can be insecure (as many Firebase projects *and* custom APIs also show). – Frank van Puffelen Sep 24 '19 at 17:53
  • okay thanks! So essentially I should just put it on client side and not worry about it until I gain more experience and understand security better – MustachedNinja Sep 24 '19 at 18:11
  • I wouldn't say you shouldn't worry about it, as that would open you up to abuse. But I doubt learning how to secure your Firebase Database is harder than learning how to write your own Node.js server. – Frank van Puffelen Sep 24 '19 at 18:50