How to Implement Server-Sent Events (SSE) for Real-Time APIs in Node.js
As of March 2026, real-time communication is a must-have for modern applications. While WebSockets dominate the conversation, Server-Sent Events (SSE) offer a simpler, HTTP-native alternative that's perfect for one-way data streams. In this guide, you'll learn how to implement SSE in Node.js with practical examples.
What Are Server-Sent Events?
Server-Sent Events is a server push technology enabling browsers to receive automatic updates from a server via HTTP. Unlike WebSockets, SSE is unidirectional - the server pushes data to the client, making it ideal for:
- Live notifications
- Stock price updates
- Social media feeds
- Progress indicators
- IoT device status
Why choose SSE over WebSockets?
- Works over standard HTTP/HTTPS
- Automatic reconnection
- No custom protocol needed
- Simpler implementation
- Works with HTTP/2
Setting Up a Basic SSE Endpoint
Create a new Node.js project and install Express:
mkdir sse-api && cd sse-api
npm init -y
npm install express cors
Here's a basic SSE endpoint in Node.js:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
// SSE endpoint
app.get('/events', (req, res) => {
// Set headers for SSE
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// Send initial connection message
res.write(`data: ${JSON.stringify({ message: 'Connected to SSE stream' })}\n\n`);
// Simulate real-time updates
const interval = setInterval(() => {
const data = {
timestamp: new Date().toISOString(),
value: Math.floor(Math.random() * 100)
};
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 2000);
// Clean up on client disconnect
req.on('close', () => {
clearInterval(interval);
res.end();
});
});
app.listen(3000, () => {
console.log('SSE server running on http://localhost:3000');
});
Consuming SSE in the Browser
Here's how to connect to your SSE endpoint from a web client:
const eventSource = new EventSource('http://localhost:3000/events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
// Update your UI here
document.getElementById('output').innerHTML =
`<pre>${JSON.stringify(data, null, 2)}</pre>`;
};
eventSource.onerror = (error) => {
console.error('EventSource failed:', error);
eventSource.close();
};
// Optional: Listen for specific event types
eventSource.addEventListener('custom-event', (event) => {
console.log('Custom event:', event.data);
});
Implementing Named Events
SSE supports named events for different data types:
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// Send notifications with named event
const notificationInterval = setInterval(() => {
res.write(`event: notification\n`);
res.write(`data: ${JSON.stringify({
id: Date.now(),
message: 'New notification!'
})}\n\n`);
}, 5000);
// Send heartbeat with different named event
const heartbeatInterval = setInterval(() => {
res.write(`event: heartbeat\n`);
res.write(`data: ${JSON.stringify({ status: 'alive' })}\n\n`);
}, 30000);
req.on('close', () => {
clearInterval(notificationInterval);
clearInterval(heartbeatInterval);
res.end();
});
});
Adding Reconnection and Retry Logic
The client can handle reconnection automatically, but you can customize it:
const eventSource = new EventSource('http://localhost:3000/events', {
withCredentials: true
});
// Customize retry interval (in milliseconds)
let retryInterval = 5000;
eventSource.onerror = (error) => {
if (eventSource.readyState === EventSource.CLOSED) {
console.log('Connection closed, retrying...');
// Implement custom retry logic if needed
setTimeout(() => {
console.log('Reconnecting...');
}, retryInterval);
}
};
Building a Production-Ready SSE Service
For production, consider these improvements:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
// Track all connected clients
const clients = new Set();
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
res.flushHeaders();
const clientId = Date.now();
const clientData = { id: clientId, res };
clients.add(clientData);
console.log(`Client ${clientId} connected. Total: ${clients.size}`);
res.write(`data: ${JSON.stringify({
type: 'connected',
clientId
})}\n\n`);
req.on('close', () => {
clients.delete(clientData);
console.log(`Client ${clientId} disconnected. Total: ${clients.size}`);
});
});
// Broadcast to all connected clients
app.post('/broadcast', express.json(), (req, res) => {
const { message } = req.body;
clients.forEach((client) => {
client.res.write(`data: ${JSON.stringify({
type: 'broadcast',
message
})}\n\n`);
});
res.json({ success: true, clients: clients.size });
});
app.listen(3000, () => console.log('Production SSE server running'));
Using SSE with Express-Sse for Simplicity
For cleaner code, use the sse-express package:
npm install sse-express
const SSE = require('sse-express');
const express = require('express');
const app = express();
app.get('/events', (req, res) => {
const sse = new SSE(res);
const interval = setInterval(() => {
sse.send({ time: new Date().toISOString() });
}, 1000);
sse.on('close', () => {
clearInterval(interval);
});
});
app.listen(3000);
Performance Considerations
- Use nginx buffering: Disable for SSE
proxy_buffering off;
proxy_cache off;
Set appropriate timeouts: SSE connections can be long-lived. Configure your load balancer accordingly.
Monitor connections: Track active connections and set limits to prevent resource exhaustion.
Use HTTP/2: Multiple SSE streams can multiplex over a single HTTP/2 connection.
When to Use SSE vs WebSockets
| Feature | SSE | WebSockets |
|---|---|---|
| Direction | Server → Client | Bidirectional |
| Protocol | HTTP | WS/WSS |
| Auto-reconnect | Built-in | Manual |
| Binary data | Base64 required | Native support |
| Browser support | Modern browsers | All modern browsers |
| Firewalls | Usually works | May be blocked |
Conclusion
Server-Sent Events provide a simple, HTTP-native way to implement real-time features in your APIs. They're perfect for one-way data streams like notifications, live updates, and monitoring dashboards. With the examples in this guide, you can start building SSE-powered features today.
For more advanced use cases, consider combining SSE with a message queue (like Redis Pub/Sub) to scale across multiple server instances.
This guide was published on March 1, 2026 as part of the 1xAPI developer education series.
Top comments (0)