Express Server-Sent Events Middleware
raw JSON →express-sse (from `dpskvn/express-sse`) is a lightweight Express.js middleware designed for easily implementing Server-Sent Events (SSE) in Node.js applications. It provides a straightforward API to send real-time, unidirectional updates from the server to connected clients over a standard HTTP connection, making it suitable for dashboards, live feeds, and notification systems. The current stable version is `1.0.0`, released on April 17, 2025, indicating active development and maintenance. Its primary differentiator is its simplicity and focus on one-way communication, which often makes it a more suitable and simpler alternative to WebSockets for server-to-client data streaming scenarios. It handles the necessary HTTP headers and connection management for SSE, allowing developers to focus on sending data without extensive boilerplate.
Common errors
error Client not receiving messages or receives all messages at once when `res.end()` is called. ↓
res.flushHeaders() is called after setting SSE-specific headers (handled by express-sse.init). Check for proxy buffering or compression middleware interfering with the stream and disable them for the SSE route. For express-sse, ensure sse.init is correctly applied to the route. error SyntaxError: Unexpected token 'export' ↓
"type": "module" in package.json), use const SSE = require('express-sse');. If using ES modules, ensure your Node.js environment supports them (Node.js >= 12 with "type": "module" or via Babel/TypeScript compilation), and use import SSE from 'express-sse';. error TypeError: Cannot read properties of undefined (reading 'init') ↓
express-sse is correctly installed (npm install express-sse) and that the SSE class is being imported/required as const SSE = require('express-sse'); or import SSE from 'express-sse'; before attempting to call new SSE(). Warnings
gotcha When deploying `express-sse` (or any SSE solution) behind reverse proxies (like Nginx, Apache) or load balancers, ensure that the proxy is configured to not buffer responses. Proxies often buffer entire responses before sending them to the client, which can prevent SSE streams from delivering real-time updates. Look for settings like `proxy_buffering off` in Nginx. ↓
gotcha Using compression middleware (e.g., `compression` package) with SSE routes can break the event stream, as the middleware will try to compress the response, effectively buffering it until completion. This defeats the purpose of real-time streaming. ↓
gotcha Browsers enforce a limit on the number of concurrent HTTP connections to a single domain (typically 6 per browser, not per tab). If multiple SSE tabs are open or other long-polling requests are made, new SSE connections might be blocked. ↓
gotcha Newlines within `data` fields in SSE messages can cause unexpected truncation or parsing errors on the client side, as `\n` signifies the end of a line in the SSE protocol, and `\n\n` signifies the end of an event. ↓
Install
npm install express-sse yarn add express-sse pnpm add express-sse Imports
- SSE wrong
const SSE = require('express-sse').SSE;correctimport SSE from 'express-sse'; - SSE (CJS) wrong
import SSE from 'express-sse';correctconst SSE = require('express-sse'); - App Middleware wrong
app.use('/stream', sse);correctapp.get('/stream', sse.init);
Quickstart
import express from 'express';
import SSE from 'express-sse';
const app = express();
const port = 3000;
// Initialize SSE with optional initial data and options
const sse = new SSE(["Initial message 1", "Initial message 2"], {
isSerialized: true, // Send initial data as separate events
initialEvent: 'initial-data' // Optional event name for initial data
});
// Set up the SSE endpoint
app.get('/stream', sse.init);
// Send events periodically
let counter = 0;
setInterval(() => {
const message = `Server time: ${new Date().toLocaleTimeString()} - Count: ${counter++}`;
sse.send(message, 'time-update', Date.now()); // content, eventName, customID
// Example of sending an error event
if (counter % 5 === 0) {
sse.send({ error: 'Heartbeat error detected' }, 'error', `error-${Date.now()}`);
}
}, 2000);
// Update initial data dynamically
app.post('/update-init', (req, res) => {
const newInitialData = req.body?.data || ["Updated initial content"];
sse.updateInit(newInitialData);
res.status(200).send('Initial data updated');
});
app.listen(port, () => {
console.log(`Express SSE server listening at http://localhost:${port}`);
console.log(`Connect to SSE stream at http://localhost:${port}/stream`);
});
/*
// Client-side JavaScript (e.g., in an HTML file):
// Ensure to include express and body-parser for the server, and a client-side HTML file to test.
// Example client.html:
// <script>
// const eventSource = new EventSource('/stream');
//
// eventSource.onopen = () => console.log('SSE connection opened.');
// eventSource.onerror = (error) => {
// console.error('SSE Error:', error);
// eventSource.close();
// };
// eventSource.onmessage = (event) => {
// console.log('Received generic message:', event.data);
// };
// eventSource.addEventListener('time-update', (event) => {
// console.log('Received time-update:', event.data, 'ID:', event.lastEventId);
// });
// eventSource.addEventListener('initial-data', (event) => {
// console.log('Received initial-data:', event.data);
// });
// eventSource.addEventListener('error', (event) => {
// console.error('Received error event:', event.data);
// });
// </script>
*/