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:
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:
- Frontend (React): https://github.com/iamdtang/eventsource-frontend
- Backend (Node.js): https://github.com/iamdtang/eventsource-backend
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.