Real-time Data Updates with Server-Sent Events (SSE) in Node.js
Tags: javascript, pubsub, eventdriven, node
Published: 2024-07-05 06:15:46

In this blog post, we'll explore how to use Server-Sent Events (SSE) to push real-time data from a server to clients. We'll create a simple example using Node.js and Express to demonstrate how SSE works.

## **What are Server-Sent Events (SSE)?**
Server-Sent Events (SSE) allow servers to push updates to the client over a single, long-lived HTTP connection. Unlike WebSockets, SSE is a unidirectional protocol where updates flow from server to client. This makes SSE ideal for live data feeds like news updates, stock prices, or notifications.

## **Creating the Server**

```
// app.js
const express = require("express");
const app = express();
const { v4 } = require("uuid");
let clients = [];

app.use(express.json());
app.use(express.static("./public"));

function sendDataToAllClients() {
  const value_to_send_to_all_clients = Math.floor(Math.random() * 1000) + 1;
  clients.forEach((client) =>
    client.response.write("data: " + value_to_send_to_all_clients + "\n\n")
  );
}

app.get("/subscribe", async (req, res) => {
  const clients_id = v4();
  const headers = {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
  };
  res.writeHead(200, headers);
  clients.push({ id: clients_id, response: res });

  // Close the connection when the client disconnects
  req.on("close", () => {
    clients = clients.filter((c) => c.id !== clients_id);
    console.log(`${clients_id} Connection closed`);
    res.end("OK");
  });
});

app.get("/data", (req, res) => {
  sendDataToAllClients();
  res.send("Data sent to all subscribed clients.");
});

app.listen(80, () => {
  console.log("Server is running on port 80");
});

```

## **Code Explanation**

- **Express Setup:** We create an Express app and set up JSON parsing and static file serving.
- **Client Management:** We maintain a list of connected clients.
- **SSE Headers:** In the /subscribe endpoint, we set the necessary headers to establish an SSE connection.
- **Send Data:** The **sendDataToAllClients** function sends random data to all subscribed clients.
- **Subscribe Endpoint:** Clients connect to this endpoint to receive real-time updates.
- **Data Endpoint:** This endpoint triggers the **sendDataToAllClients** function to send data.

## **Creating the Client**
Next, let's create a simple HTML page to subscribe to the server and display the real-time data.

```
<!-- index.html  -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>SSE - Example (Server-Sent-Events)</title>
</head>
<body>
  <div id="data"></div>
</body>
</html>

<script>
  const subscription = new EventSource("/subscribe");

  // Default events
  subscription.addEventListener("open", () => {
    console.log("Connection opened");
  });

  subscription.addEventListener("error", () => {
    console.error("Subscription err'd");
    subscription.close();
  });

  subscription.addEventListener("message", (event) => {
    console.log("Receive message", event);
    document.getElementById("data").innerHTML += `${event.data}<br>`;
  });
</script>

```

## Code Explanation
- **EventSource:** We create a new **EventSource** object to subscribe to the /subscribe endpoint.
- **Event Listeners:** We set up listeners for open, error, and message events.
- **Display Data:** When a message is received, we append the data to a div.


## **Running the Example**
1. Start the server:
`node app.js`

2. Open your browser and navigate to http://localhost/subscribe. [Don't close it]
3. Now, open another tab & navigate to http://localhost/data and you should see random data prints onto the screen in other tab.
4. You can subscribe to multiple clients/tabs as you want & you can simply navigate to http://localhost/data & you would see the same data emits to all the subscribed clients.


## **Conclusion**
In this post, we've seen how to use Server-Sent Events (SSE) to push real-time updates from a server to connected clients using Node.js and Express. SSE is a simple yet powerful way to add real-time capabilities to your web applications without the complexity of WebSockets.

## **Warning⚠️:**
When not used over HTTP/2, SSE suffers from a **limitation** to the **maximum number of open connections**, which can be especially painful when opening multiple tabs, as the limit is per browser and is set to a very low number (6). The issue has been marked as "Won't fix" in [Chrome](https://bugs.chromium.org/p/chromium/issues/detail?id=275955) and [Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=906896). This limit is per browser + domain, which means that you can open 6 SSE connections across all of the tabs to `www.example1.com` and another 6 SSE connections to `www.example2.com` (per [Stackoverflow](https://stackoverflow.com/questions/5195452/websockets-vs-server-sent-events-eventsource/5326159)). When using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100).

Happy coding!

## **Resources:**
- https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events