Server-Sent Events ยท Astro Tech Blog

Server-Sent Events

Server-Sent Events (SSE) let the server push data to the client over a single HTTP connection. Simpler than WebSocket โ€” one-directional (server โ†’ client) over standard HTTP.

How SSE Works

Client                          Server
  โ”‚                               โ”‚
  โ”œโ”€โ”€ GET /events โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚  (standard HTTP)
  โ”‚                               โ”‚
  โ”‚โ—€โ”€โ”€ event: message โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
  โ”‚โ—€โ”€โ”€ data: {"msg":"Hello"} โ”€โ”€โ”€โ”€โ”‚
  โ”‚โ—€โ”€โ”€                           โ”‚
  โ”‚โ—€โ”€โ”€ event: update โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
  โ”‚โ—€โ”€โ”€ data: ... โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
  โ”‚                               โ”‚
  โ”‚โ—€โ”€โ”€ (connection stays open) โ”€โ”€โ”‚  (auto-reconnect)

Basic Usage

const source = new EventSource('/api/events');

source.onmessage = function(event) {
  console.log('Received:', event.data);
};

source.onopen = function() {
  console.log('Connection opened');
};

source.onerror = function() {
  console.error('Connection error');
};
Demo: Server-Sent Events Demo
HTML
<div>
<p>Simulated server sends events at random intervals.</p>
<div style='display:flex;gap:8px;margin-bottom:8px;'>
<button id='sse-start'>Start SSE</button>
<button id='sse-stop' disabled>Stop</button>
</div>
<div style='max-height:180px;overflow-y:auto;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:8px;'>
<div id='sse-messages' style='font-family:monospace;font-size:0.85rem;'></div>
</div>
<pre id='sse-status' style='background:#f1f5f9;padding:8px;border-radius:4px;margin-top:8px;'></pre>
</div>
JavaScript
const messages = document.getElementById('sse-messages');
const status = document.getElementById('sse-status');
let eventCount = 0;
let intervalId = null;

document.getElementById('sse-start').onclick = function() {
this.disabled = true;
document.getElementById('sse-stop').disabled = false;
status.textContent = '๐Ÿ”„ SSE connected โ€” waiting for events...';

// Simulate server-sent events
intervalId = setInterval(() => {
eventCount++;
const types = ['update', 'notification', 'alert', 'status'];
const type = types[Math.floor(Math.random() * types.length)];
const data = {
id: eventCount,
type: type,
message: type + ' event #' + eventCount,
timestamp: new Date().toLocaleTimeString()
};

const div = document.createElement('div');
div.textContent = '๐Ÿ“ฉ ' + type + ': ' + data.message;
messages.insertBefore(div, messages.firstChild);
status.textContent = 'โœ… Received event #' + eventCount + ' (' + type + ')';

// Limit messages shown
while (messages.children.length > 20) {
messages.removeChild(messages.lastChild);
}
}, 1000 + Math.random() * 3000);
};

document.getElementById('sse-stop').onclick = function() {
clearInterval(intervalId);
intervalId = null;
this.disabled = true;
document.getElementById('sse-start').disabled = false;
status.textContent = 'โน๏ธ SSE disconnected (' + eventCount + ' events received)';
};
Live Output Window

Server Format

The server sends a special text format:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

Data lines:

data: {"message": "Hello"}\n\n

event: update\ndata: {"id": 1}\n\n

event: custom\ndata: line1\ndata: line2\n\n

id: 42\ndata: message\n\n

retry: 5000\n\n

Event Fields

FieldDescription
data:The message payload (can be multiple lines)
event:Custom event type (defaults to message)
id:Last event ID (for reconnection resume)
retry:Reconnection time in milliseconds

Listening to Named Events

const source = new EventSource('/events');

source.addEventListener('userConnected', function(event) {
  console.log('User connected:', event.data);
});

source.addEventListener('newMessage', function(event) {
  console.log('New message:', JSON.parse(event.data));
});

// Default listener for unnamed events
source.onmessage = function(event) {
  console.log('Generic event:', event.data);
};

The server sends:

event: userConnected
data: {"userId": 42}

event: newMessage
data: {"text": "Hello"}

SSE vs WebSocket

FeatureSSEWebSocket
DirectionServer โ†’ Client onlyBidirectional
ProtocolHTTPWebSocket
Auto-reconnectโœ… Built-inโŒ Manual
Binary dataโŒ Text onlyโœ… Both
Browser supportMost modernAll modern
SimplicityVery simpleModerate
Max connections6 per browserUnlimited

Auto-Reconnection

SSE automatically reconnects when the connection drops. If the server sends an id field, the browser sends Last-Event-ID header on reconnect:

// Browser sends:
// GET /events
// Last-Event-ID: 42

// Server can resume from event 43

Connection Management

// Close connection
source.close();

// Check state
source.readyState // 0=CONNECTING, 1=OPEN, 2=CLOSED

Practical: Live Notifications

SSE is ideal for:

  • Live notifications (new messages, friend requests)
  • Stock tickers (price updates)
  • News feeds (new articles)
  • Log streaming (server logs in real-time)
  • Progress updates (build/deploy status)

Key Takeaways

  • SSE is server โ†’ client only โ€” simpler than WebSocket for push notifications
  • Use new EventSource(url) to connect
  • Listen with onmessage for generic events, addEventListener for named events
  • Auto-reconnects built-in โ€” includes Last-Event-ID for resume
  • Server format is text/event-stream with data:, event:, id:, retry: fields
  • SSE works over standard HTTP โ€” no special server required
  • Use WebSocket when you need bidirectional communication; use SSE for server push