HTML5 - Server-Sent Events
HTML5 - Server-Sent Events (SSE)
Section titled “HTML5 - Server-Sent Events (SSE)”Server-Sent Events (SSE) is a standard enabling web servers to push data updates to web clients (browsers) over a single, long-lived HTTP connection. Unlike WebSockets (which are bidirectional), SSE is unidirectional: data flows only from the server to the client.
This makes SSE a simpler alternative to WebSockets when you only need the server to send updates to the client, such as live news feeds, stock tickers, status updates, notifications, or monitoring dashboards.
SSE uses a standard HTTP connection and a specific text-based event stream format, making it potentially easier to implement on some server infrastructures compared to WebSockets and more resilient across proxies that might interfere with WebSocket connections.
Client-Side Implementation (JavaScript)
Section titled “Client-Side Implementation (JavaScript)”On the client-side, you use the EventSource interface to connect to an SSE endpoint.
// Create an EventSource instance, connecting to the server endpointconst source = new EventSource('/sse-endpoint'); // Replace with your actual endpoint URLThe browser will automatically attempt to reconnect if the connection is lost.
Handling Events
Section titled “Handling Events”You listen for messages using event listeners attached to the EventSource object:
onopen: Fired when the connection to the server is successfully established.onmessage: Fired when a message without a specific event name is received from the server. The event object’sdataproperty contains the message payload.onerror: Fired if an error occurs (e.g., connection failure). Note: This doesn’t fire for HTTP error status codes like 404 or 500 initially, only if the connection breaks.- Custom Events: If the server sends events with specific names (using the
event:field), you listen for them usingaddEventListener('eventName', handler).
Client Example
Section titled “Client Example”<!DOCTYPE html><html><head> <title>Server-Sent Events Example</title></head><body> <h1>Server Time Updates (SSE)</h1> <div id="timeDisplay">Waiting for server time...</div> <div id="status">Connecting...</div>
<script> const timeDisplay = document.getElementById('timeDisplay'); const statusDiv = document.getElementById('status');
// Check for browser support if (typeof(EventSource) !== "undefined") { // Replace '/get-time-sse' with your actual server endpoint const source = new EventSource('/get-time-sse');
statusDiv.textContent = 'Connecting to event source...';
// Called when the connection is opened source.onopen = function(event) { statusDiv.textContent = 'Connection opened!'; console.log("SSE Connection opened.", event); };
// Called for messages with no specific event name (default) source.onmessage = function(event) { // Assuming the server sends time as plain text in the data field timeDisplay.textContent = `Server time: ${event.data}`; console.log("SSE Message received:", event.data); };
// Example: Listening for a custom named event 'userUpdate' source.addEventListener('userUpdate', function(event) { console.log("Custom 'userUpdate' event received:", event.data); // Process user update data (e.g., parse JSON) // const userData = JSON.parse(event.data); // updateUI(userData); }, false);
// Called when an error occurs (e.g., connection lost) source.onerror = function(event) { statusDiv.textContent = 'Connection error. Reconnecting...'; console.error("SSE Error occurred:", event); // The browser will automatically attempt to reconnect. // If the server explicitly closes the connection (e.g., HTTP 204), // or if readyState becomes CLOSED, you might want to stop reconnecting: if (source.readyState === EventSource.CLOSED) { statusDiv.textContent = 'Connection closed permanently by server.'; source.close(); // Ensure it's closed client-side too } };
} else { statusDiv.textContent = 'Sorry, your browser does not support Server-Sent Events.'; } </script></body></html>Closing the Connection
Section titled “Closing the Connection”You can explicitly close the connection from the client-side using the close() method:
// Close the connectionsource.close();console.log("SSE connection closed by client.");Server-Side Implementation
Section titled “Server-Side Implementation”The server-side script needs to:
- Set the
Content-Typeheader totext/event-stream. - Set appropriate caching headers (e.g.,
Cache-Control: no-cache) to prevent intermediate proxies from buffering the response. - Keep the connection open.
- Send data formatted according to the SSE specification.
SSE Message Format
Section titled “SSE Message Format”Messages are plain text, separated by two newline characters (\n\n). Each message can consist of one or more fields, each ending with a single newline (\n). Common fields are:
data: <message content>: The actual data payload for the event. Can span multiple lines if each starts withdata:.event: <event name>: Specifies a name for the event. If omitted, the message triggers theonmessagehandler on the client.id: <unique id>: Sets a unique ID for the event. If the connection drops, the browser will send aLast-Event-IDHTTP header with the last received ID, allowing the server to resume the stream.retry: <milliseconds>: Specifies how long the client should wait before attempting to reconnect if the connection is lost (in milliseconds).
Conceptual Node.js Server Example
Section titled “Conceptual Node.js Server Example”This basic Node.js example sends the current server time every second.
// server.js (Node.js example)const http = require('http');
const server = http.createServer((req, res) => { // Route for the SSE endpoint if (req.url === '/get-time-sse' && req.method === 'GET') {
// Set headers for SSE res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', // Important to keep connection open 'Access-Control-Allow-Origin': '*' // Optional: Add CORS header if needed });
console.log('Client connected to SSE endpoint.');
// Send an initial message immediately (optional) res.write(`data: Server connected at ${new Date().toLocaleTimeString()}\n\n`);
// Send time updates every second const intervalId = setInterval(() => { const currentTime = new Date().toLocaleTimeString(); // Simple message (triggers 'onmessage') res.write(`data: ${currentTime}\n\n`);
// Example of sending a named event and an ID // res.write(`id: ${Date.now()}\n`); // res.write(`event: serverTimeUpdate\n`); // res.write(`data: ${JSON.stringify({ time: currentTime })}\n\n`);
}, 1000);
// Handle client disconnect req.on('close', () => { console.log('Client disconnected from SSE endpoint.'); clearInterval(intervalId); // Stop sending updates res.end(); // End the response });
} else { // Handle other requests (e.g., serve the HTML page) res.writeHead(404); res.end('Not Found'); }});
const PORT = 3000;server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}`);});To run this, save it as server.js and run node server.js. Then, access the HTML page (you’d need to serve that separately or add another route) and update its EventSource URL to http://localhost:3000/get-time-sse.
SSE vs. WebSockets
Section titled “SSE vs. WebSockets”- Directionality: SSE is server-to-client only; WebSockets are bidirectional.
- Protocol: SSE uses standard HTTP(S); WebSockets use a different protocol (ws/wss) after an initial HTTP handshake.
- Complexity: SSE is generally considered simpler to implement, especially on the client-side.
- Overhead: SSE typically has less protocol overhead per message than WebSockets.
- Proxy/Firewall Compatibility: SSE often works better through restrictive proxies that might block WebSocket connections.
- Built-in Features: SSE has built-in support for automatic reconnection and event IDs, which need manual implementation with WebSockets.
Choose SSE when you primarily need to push updates from the server to the client. Choose WebSockets for full real-time, two-way communication.