SSE
Point Flux at one endpoint that speaks standard Server-Sent Events and it takes care of turning that stream into live store updates for every channel — a single connection per device, single-connection coordination across tabs, backfill driven by per-channel anchors, and optional POST reconciliation all come for free once the adapter is wired in.
- A backend serving one Server-Sent Events endpoint that follows the contract below — any language or framework
- @tsworldtech/flux and @tsworldtech/flux_sse installed
Create the adapter
createSseAdapter() takes one url: a single endpoint that serves every channel. The stream opens once a store registers, and it carries all registered channels together.Register a store
channel is the name your server tags each change with, and event is the SSE event name your server sends that channel's changes under — commonly 'UPDATE'. Register as many channels as you like; they all share the one stream.One stream per device
Only the leader tab opens the stream; other tabs open nothing and receive data through the cross-tab channel, so extra tabs add no server load. And because every channel shares that one stream, a device holds a single SSE connection no matter how many stores are registered. This also keeps you under the browser's limit on concurrent connections per origin (about six on HTTP/1.1), which per-channel streams would otherwise eat into.
The server contract
Without the flux:backfill event, Flux can't tell "nothing changed" from "the catch-up never finished", so it can't confirm a channel is current and falls back to refetching. Send it for every channel on every request, even when there are no rows to replay.
Flux never invents an id or a timestamp. A backfill row without them is counted as lost, the catch-up for that channel reports failed, and Flux refetches the channel instead of recording it as current. If you see repeated refetches after reconnects, check that your rows carry both.
A reference route
What catch-up proves
Registering channels after the stream is open
Deletion reconciliation (POST)
reconcileUrl is configured:Reconnects and catch-up are automatic
You never need to write reconnect logic yourself. If the connection drops — network blip, tab backgrounded, server restart — the browser retries on its own, and if the stream is closed for good Flux opens a fresh one on the next reconnect, with each channel's sync anchor in the URL so your server can run a targeted changelog query and stream the backfill. Your server only has to honor the anchors it is sent and finish each channel's backfill with the marker.
What a single stream means for failures
Any backend that speaks SSE
Anything that can hold an HTTP connection open and write text/event-stream frames works here — Node, Python, Go, Rails, PHP, whatever you already run. Flux only ever talks to a plain URL over standard SSE; there is no required backend framework or language.
Authentication
The browser's native EventSource cannot attach custom headers, so a bearer token in an Authorization header is not an option. The common patterns are a short-lived signed token in the url's query string, or same-origin cookies if your app and API share a domain.
Building the server side of this contract from scratch? See getting_started/node_sse for a full worked example. For what happens during reconnect specifically, see core_concepts/sync_anchors and core_concepts/activity_bus.