FSD Frontend System Design

Part I β€” The Pipes Β· Lesson 2.5

Server Side Events

In one lineA one-way stream over ordinary HTTP that reconnects and resumes by itself.

Where you've seen itA live cricket score ticker. Thousands of viewers, no one sending anything back β€” exactly the shape SSE is for.

  • Time14 min
  • DiagramOne-way event stream + the wire format + auto-resume
  • Chapter2 Β· Communication

Diagram

diagram
ONE REQUEST, MANY RESPONSES

  Client                          Server
    │── GET /scores ─────────────►│  Accept: text/event-stream
    │◄─ 200, Content-Type: text/event-stream
    β”‚                             β”‚  (response never ends)
    │◄─── event 1 ────────────────│
    │◄─── event 2 ────────────────│
    │◄─── event 3 ────────────────│
    β”‚        βœ— network drops      β”‚
    │── GET /scores ─────────────►│  browser retries automatically
    β”‚   Last-Event-ID: 3          β”‚  ← sent for you, no code
    │◄─── event 4 ────────────────│  server resumes from 3
diagram
THE WIRE FORMAT β€” plain text, newline delimited

  id: 42
  event: score
  data: {"runs":186,"wickets":4}
  ⏎                              ← blank line ends the event

  retry: 5000                    ← server sets the reconnect delay
diagram
SSE vs WEBSOCKET β€” same latency, different bill

              SSE                        WebSocket
  direction   server β†’ client only       both ways
  protocol    plain HTTP (cacheable      custom framing after
              infra, proxies, HTTP/2)     the upgrade
  reconnect   built into the browser     you write it
  resume      Last-Event-ID, free        you write it
  binary      no (text only)             yes

How it works

  1. Client opens new EventSource('/scores') β€” a normal GET that stays open.
  2. Server streams text events, each ending in a blank line, and never closes the response.
  3. Every event carries an id:; the browser remembers the last one seen.
  4. On a drop, the browser reconnects on its own and sends Last-Event-ID β€” the server replays from there.
  5. Client listens per event type: es.addEventListener('score', …).
js
const es = new EventSource('/scores');
es.addEventListener('score', (e) => render(JSON.parse(e.data)));
es.onerror = () => {/* browser is already retrying β€” don't reconnect here */};

Trade-offs

Use whenAvoid when
Only the server has newsThe client must send too β†’ WebSocket
Feeds, tickers, notifications, logsYou need binary frames
You want reconnect + resume for freeMany streams per tab on HTTP/1.1
Streaming LLM tokens to the UIβ€”

The HTTP/1.1 six-connection limit: each EventSource occupies one of the browser's six connections per origin, and an open stream never frees it. Over HTTP/2 the limit effectively disappears β€” this alone is a reason to be on HTTP/2 before shipping SSE.

Interview angle

"Score ticker for ten million viewers β€” SSE or WebSocket?"

  • SSE: the client never sends anything, so duplex is wasted capacity and wasted code.
  • Reconnect and replay come free via Last-Event-ID, which is the hard part of a WebSocket build.
  • It's plain HTTP, so it rides existing CDN, proxy, and HTTP/2 infrastructure β€” but confirm HTTP/2 first.

Recap

  • One-way, text-only, plain HTTP, streamed over a response that never ends.
  • Auto-reconnect and Last-Event-ID resume are the killer features.
  • Default to SSE for feeds; upgrade to WebSocket only when the client must talk back.