Build an SSE stream

Resume after a disconnect

Use event identifiers and a snapshot fallback so reconnection cannot silently create a gap.

EventSource reconnects for free. That is not the same as “the client still has every incident.” If the server dropped events while the tab was offline, a green reconnect badge hides a gap.

You need event ids, a bounded replay log, and a snapshot fallback when replay is no longer possible.

What the browser sends

After the first event, reconnect requests include the last id:

GET /events HTTP/1.1
Last-Event-ID: evt_84

If incident 42 was the last applied event on the client, the server must replay everything after evt_84 or refuse honestly.

Server replay with a cap

We retain the last five hundred events in memory for the tutorial board:

const retained = new Map()

function replayFrom(res, lastId) {
  const ids = [...retained.keys()]
  const start = ids.indexOf(lastId)

  if (start === -1) {
    res.write('event: resync.required\n')
    res.write('data: {"reason":"history expired"}\n\n')
    return
  }

  for (const id of ids.slice(start + 1)) {
    res.write(retained.get(id))
  }
}

When start === -1, the client listens for resync.required and fetches GET /api/incidents. Compare that snapshot to the on-screen board. They must match.

Test the gap on purpose

  1. Open the board and note evt_84.
  2. Kill the network or close the laptop lid for thirty seconds.
  3. Create three incidents on the server.
  4. Restore the network.

If replay works, the board shows all three without a manual refresh. If replay expired, the resync event fires and the snapshot route repaints state.

Never treat “connected again” as success without comparing to a fresh snapshot. That comparison is the evidence worth saving.

Browser devtools check

In Chrome, open Network, filter EventStream, and watch the Last-Event-ID request header after you yank the cable. If the header is missing, the client never applied an id and replay cannot work.

Try this on your own project: disconnect, mutate server state, reconnect, and prove the final UI matches GET /api/incidents.

Lesson completed