Real-Time Updates with SSE
EventSource API, reconnection, broadcasting, and Express/Fastify SSE
What You’ll Build
After following this guide, you will have a working implementation of real-time updates with sse in your project. Server-Sent Events (SSE) is the simplest way to push real-time updates from server to client. Unlike WebSockets, SSE works over plain HTTP, auto-reconnects, and is supported in all browsers. Perfect for live dashboards, notifications, progress updates, and AI streaming responses.
Use Cases & Problems Solved
- Build reliable server endpoints that clients can consume consistently
- Handle common backend patterns like routing, middleware, and error handling
- Provide a clean interface between your frontend and data layer
Prerequisites
- Node.js 18+
- Express.js
Step-by-Step Implementation
SSE endpoint on Express
The following snippet shows how to sse endpoint on express. Copy this into your project and adjust the values for your environment.
const clients = new Set();
app.get('/api/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
// Send initial connection event
res.write('data: {"type":"connected"}\
\
');
clients.add(res);
req.on('close', () => clients.delete(res));
});
// Broadcast to all connected clients
function broadcast(eventName, data) {
const message = \`event: \${eventName}\
data: \${JSON.stringify(data)}\
\
\`;
for (const client of clients) {
client.write(message);
}
}
// Example: call this when a new order is created
app.post('/api/orders', async (req, res) => {
const order = await createOrder(req.body);
broadcast('new-order', order);
res.json(order);
});
Client-side EventSource with auto-reconnect
The following snippet shows how to client-side eventsource with auto-reconnect. Copy this into your project and adjust the values for your environment.
const eventSource = new EventSource('/api/events');
eventSource.addEventListener('new-order', (event) => {
const order = JSON.parse(event.data);
console.log('New order:', order);
// Update UI
});
eventSource.onerror = () => {
console.log('SSE connection lost, auto-reconnecting...');
// EventSource automatically reconnects!
};
⚠️ Don’t Do This
❌ Polling the server every second for updates
// Wastes bandwidth and server resources!
setInterval(async () => {
const data = await fetch('/api/orders/latest').then(r => r.json());
updateUI(data);
}, 1000); // 86,400 requests per day PER USER
✅ Use SSE — server pushes updates only when they happen
// ONE persistent connection, updates only when data changes
const es = new EventSource('/api/events');
es.addEventListener('new-order', (e) => updateUI(JSON.parse(e.data)));
Testing
Add these tests to verify your API endpoints work correctly:
// __tests__/api.test.ts
import { describe, it, expect } from 'vitest';
describe('Real-Time Updates with SSE', () => {
it('should return 200 for valid requests', async () => {
const res = await fetch('/api/endpoint', { method: 'GET' });
expect(res.status).toBe(200);
const data = await res.json();
expect(data).toBeDefined();
});
it('should return 400 for invalid input', async () => {
const res = await fetch('/api/endpoint', {
method: 'POST',
body: JSON.stringify({}),
});
expect(res.status).toBe(400);
});
it('should handle errors gracefully', async () => {
const res = await fetch('/api/endpoint/nonexistent');
expect(res.status).toBe(404);
});
});
Verification
# Open browser to your app with DevTools Network tab
# Filter by EventStream
# Should see persistent connection with events flowing
# Create an order in another tab — event should appear instantly Related Specs
WebSocket Chat with Socket.io
Rooms, namespaces, auth middleware, reconnection, and scaling
Secure Webhook Handler
Signature verification, idempotency, retry handling, and queue processing
Production-Ready REST API (Express)
Error handling, Zod validation, rate limiting, CORS, and structured logging