Lesson 0003 · ~10 minutes
One skill: read a text/event-stream chunk the way the
browser does, and know what reconnect
actually guarantees.
In a design review you can say: “Auto-reconnect is free;
at-least-once resume without gaps is a server contract using
id + Last-Event-ID.”
The server keeps an HTTP response open with
Content-Type: text/event-stream and writes UTF-8 lines. The
format is defined in the
WHATWG parsing section. An event is a block of fields ending in a
blank line.
data: hello
data: multi-line starts here
data: second line
event: job-progress
id: 17
data: {"pct":40}
: keep-alive comment every ~15s
retry: 3000
data: after setting retry
Lines starting with : are comments (ignored) — useful as
heartbeats so idle proxies do not kill the connection
(authoring notes).
| Wire | Client sees |
|---|---|
data: (one or more) |
event.data string; multiple lines joined with
\n
|
no event: |
type message → onmessage /
"message" listener
|
event: name |
type name → only
addEventListener("name")
|
id: value |
updates last event ID;
event.lastEventId on this and later events until
changed
|
retry: ms |
sets UA reconnect delay (digits only); not exposed as a DOM event |
One space after the colon is optional and stripped
(data:x and data: x are the same value).
Unknown field names are ignored.
Two different promises people conflate:
Connection drops → wait (default ~few seconds, or last
retry:) → reconnect. If a last event ID is known, send
request header Last-Event-ID.
Spec: Last-Event-ID.
Read Last-Event-ID, decide what to emit next, keep a
durable log or offset, stamp every event with a new
id:. Without that, reconnect only means “subscribe from
now” — gaps are possible.
Ask: “Do we need gapless delivery after blips?” If yes, budget for an
event store / cursor, not just EventSource. If “latest
state is enough,” skip IDs and send full snapshots or accept gaps.
Empty id field (name present, empty value) clears the last
event ID so the next reconnect omits the header — useful if you
intentionally reset the cursor (see
spec examples).
What does the browser dispatch for each finished block?
Wire ends with a blank line:
data: alpha
data: beta
(blank)
event: score
data: 3
(blank)
Which client handler runs?
Server writes only:
: ping
(blank)
Client received id: 7 then the TCP drop. On reconnect,
what is true?
Live “seats left” counter; showing a slightly old number after a 2s blip is fine. Resume strategy?
Trading blotter: every fill event must appear in order after reconnect. What do you require?
Events are rare; corporate proxy drops idle HTTP after ~30s. Cheap fix on the stream?
data / event / id /
retry / : comments — that is almost the
whole language.
id + server policy on
Last-Event-ID.
WHATWG — Interpreting an event stream (field rules + worked examples). Readable companion: MDN Using server-sent events (fields table and reconnect notes).