Streaming live data to your ReactJS app using Server-Sent-Events (SSE)

December 16, 2024 (2w ago)

Have you ever wanted to stream data to your ReactJS app in real time? Maybe show a popup message or shut down the app completely without waiting for a polling solution?

As it turns out, the solution existed since around 2010 and is now fully supported in all major browsers.

Let’s explore Server-Sent Events (SSE), compare them to other real-time data solutions, and implement a robust example using a modern ReactJS app created with Vite.


Overview of Real-Time Data Solutions

When talking about real-time data solutions for ReactJS (or any other JS application), we have a few options:

Here’s how they compare:

Feature/Criteria Breakdown

1. Real-Time

2. Ease of Implementation

3. Browser Support

4. Efficiency

5. Bi-Directional Communication

6. Reconnection Handling

7. Use Case Examples

If your use case involves getting events from the server only, SSE is a simple, efficient, and reliable solution.


Backend Implementation

We’ll use an Express.js server for the backend. Follow these steps:

Basic Express Setup

Start with a basic Express server:

const express = require("express");
const app = express();
const PORT = 3010;
 
app.use(express.json());
 
app.get("/events", (req, res) => {
  res.status(200).send("Event route is working");
});
 
app.listen(PORT, () => {
  console.log(`SSE server running on http://localhost:${PORT}`);
});

Adding SSE-Specific Headers

Add headers to enable SSE:

app.get("/events", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
 
  res.write(`data: Connected to SSE\n\n`);
});

Sending Periodic Messages

Send updates periodically to connected clients:

app.get("/events", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
 
  res.write(`data: Connected to SSE\n\n`);
 
  const interval = setInterval(() => {
    const message = { time: new Date().toISOString() };
    res.write(`data: ${JSON.stringify(message)}\n\n`);
  }, 2000);
 
  req.on("close", () => {
    clearInterval(interval);
    res.end();
  });
});

Enabling CORS

To avoid cross-origin issues, add:

const cors = require("cors");
app.use(cors());

Frontend Implementation Using Vite and ReactJS

Setting Up the Project

Create a new Vite project with ReactJS and TypeScript:

npm create vite@latest sse -- --template react-ts
cd sse
npm install

Setting Up the Component

Edit the App.tsx file to include useEffect and useState hooks:

import { useEffect, useState } from "react";
import "./App.css";
 
function App() {
  const [messages, setMessages] = useState("");
 
  useEffect(() => {
    const eventSource = new EventSource("http://localhost:3010/events");
 
    eventSource.onmessage = (event) => {
      console.log("Received event:", event.data);
      setMessages(event.data);
    };
 
    eventSource.onerror = (error) => {
      console.error("EventSource failed:", error);
    };
 
    return () => {
      eventSource.close();
    };
  }, []);
 
  return (
    <div>
      <h1>Real-Time Messages</h1>
      <p>{messages}</p>
    </div>
  );
}
 
export default App;

Advanced Topics

Security Considerations

  1. Authentication: Secure your SSE endpoint using tokens:
app.get('/events', authenticate, (req, res) => { ... });
  1. Rate Limiting: Use middleware like express-rate-limit to prevent abuse.

Scaling SSE

  1. Load Balancers: Use NGINX or HAProxy to efficiently manage SSE connections.
  2. Connection Limits: Implement logic to cap active connections for better scalability.

Debugging SSE

  1. Use curl to test your endpoint:
curl http://localhost:3010/events
  1. Monitor browser DevTools for network activity and messages.

Custom Event Types

SSE supports custom event types using the event: keyword:

res.write(`event: customEvent\n`);
res.write(`data: {"info": "custom event triggered"}\n\n`);

Handle them on the frontend:

eventSource.addEventListener("customEvent", (event) => {
  console.log("Custom Event:", event.data);
});

Conclusion

With Vite and ReactJS as the frontend framework and a lightweight Express backend, we’ve implemented a robust, scalable real-time solution using Server-Sent Events. The combination of simplicity, efficiency, and modern browser support makes SSE an excellent choice for unidirectional real-time data delivery.

Originally published on DEV.to