1

I would use data stream to update automatically my angular material datatable when data get pushed to the database

Here's my router.js

router
  .route("/")
  .get(function (req, res, err) {

    // Get a database reference to our posts
    var db = admin.database();
    var ref = db.ref("/");

    // Attach an asynchronous callback to read the data at our posts reference
    ref.on("value", function (snapshot) {
      var list = [];
      snapshot.forEach(function (elem) {
        list.push(elem.val());
      })


      res.send(list);

    }, function (errorObject) {
      console.log("The read failed: " + errorObject.code);
    });
  });

router
  .route("/")
  .post(function (req, res, err) {
    console.log(req.body);
    // Get a database reference to our posts
    var db = admin.database();
    var ref = db.ref("/");

    // Attach an asynchronous callback to read the data at our posts reference
    ref.push(
      {
        "text": req.body.text
      }

    );

  });

When I run my app I get all my data, then when I try to post data to the database I get this error :

FIREBASE WARNING: Exception was thrown by user callback. Error: Can't set headers after they are sent.

When I use once instead of on I don't get error, but as I need to fetch for new data every update on database, I should use on to get a data stream.

So how can I use on without getting this error ?

Hamza Haddad
  • 1,516
  • 6
  • 28
  • 48

1 Answers1

5

When you attach a listener with on("value"), it will keep listening for the data. This means that it will also fire if the data changes later, at which point you've long sent a response to the client and closed the socket.

To prevent this, you should listen with once("value"):

router
  .route("/")
  .get(function (req, res, err) {

    // Get a database reference to our posts
    var db = admin.database();
    var ref = db.ref("/");

    // Attach an asynchronous callback to read the data at our posts reference
    ref.once("value", function (snapshot) {
      var list = [];
      snapshot.forEach(function (elem) {
        list.push(elem.val());
      })

      res.send(list);

    }, function (errorObject) {
      console.log("The read failed: " + errorObject.code);
      res.status(500).send(errorObject.code);
    });
  });

I also added a response to the error handler.

Update

If you want to keep sending updates to the client, you'll want to keep the connection open. In that case you shouldn't call res.send() (which closes the connection), but res.write() (which leaves the connection open). See Difference between response.send and response.write in node js

router
  .route("/")
  .get(function (req, res, err) {

    // Get a database reference to our posts
    var db = admin.database();
    var ref = db.ref("/");

    // Attach an asynchronous callback to read the data at our posts reference
    ref.on("value", function (snapshot) {
      var list = [];
      snapshot.forEach(function (elem) {
        list.push(elem.val());
      })


      res.write(list);

    }, function (errorObject) {
      console.log("The read failed: " + errorObject.code);
      res.status(500).send(errorObject.code);
    });
  });
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • As I said that will not create a data stream . I mean in front end I have an observable that listen to my get http request. I would that when I post a data in database, my observable update the view automatically. With once it's not possible. – Hamza Haddad Feb 23 '18 at 05:44
  • Calling `send()` closes the connection after the write. To keep the connection open, use `write()`. But in general for this type of approach, consider using Web Sockets instead of plain HTTP. – Frank van Puffelen Feb 23 '18 at 14:11
  • Now I get this error with write `FIREBASE WARNING: Exception was thrown by user callback. TypeError: First argument must be a string or Buffer` – Hamza Haddad Feb 23 '18 at 21:04
  • I guess that means you can only write `res.write(JSON.stringify(list))` – Frank van Puffelen Feb 23 '18 at 21:20