Streaming Data Over HTTP with Server-Sent Events

Last reviewed on November 25, 2021

Recently I learned about server-sent events, which allow for a server to send new data to the browser at any time by pushing messages over a persistent connection. This persistent connection can be created with the browser's EventSource class.

In this post, I wanted to show a simple counter application using this technique, where the state of the count is stored in-memory on the server and each increment is streamed to the frontend.

Here is a full demo where you can see this in action: https://eventsource-counter-demo.surge.sh/.

If you open up the Network tab in Chrome Dev Tools and click on the /count URL, you can see the data being streamed in:

Network panel

The Frontend

For the frontend I used React. I did everything in the App component in just a few lines.

import { useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const source = new EventSource("http://localhost:7000/count");

    source.addEventListener("open", () => {
      console.log("EventSource connection opened");
    });

    source.addEventListener("message", message => {
      setCount(message.data);
    });

    source.addEventListener("error", error => {
      console.error("Error: ", error);
    });

    return () => {
      source.close();
    };
  }, []);

  return <div>{count}</div>;
}

The Backend

For the backend, I used Node.js and Express.

const express = require("express");
const cors = require("cors");

const app = express();

app.use(cors());

app.get("/count", function (request, response) {
  let count = 0;

  response.writeHead(200, {
    Connection: "keep-alive",
    "Content-Type": "text/event-stream",
  });

  setInterval(() => {
    count++;
    response.write(`data: ${count}\n\n`);
  }, 1000);
});

app.listen(7000);

Each streamed message can have a combination of the following fields:

  • id
  • data
  • retry
  • event

In this example, I'm only using the data field. Each field must be on its own line, and each message must have a line in between them, hence the two newline characters (\n\n). For example, the messages streamed in this application look like the following:

data: 1

data: 2

data: 3

You can read more about the other fields for messages here.

All of the code to get this application running can be found at the following repos:

Shout-out to Eric for writing the blog post, Implement Server-Sent Events in Node.js and React, where I learned about all of this.