Connection
The event stream is one long-lived HTTP response of
server-sent events. Open it with the
browser's built-in EventSource; you need no library.
Endpoint
| Method and path | GET /api/stream |
| Response type | text/event-stream |
| Reconnect delay | retry: 1000 — one second, sent first on every connection |
| Keepalive | a comment line, : keepalive, after 15 seconds with nothing else sent |
| Event names | none: every event arrives as a plain message |
Each event is one data: line holding a JSON object, the envelope. The stream sends only
events you are allowed to see; the event catalog says who receives each entity.
Keepalives
The keepalive is an SSE comment, so EventSource never surfaces it as an event. It keeps proxies and
load balancers from closing a quiet connection.
Reconnecting
When the connection drops, EventSource reconnects by itself after the one-second retry delay, the
standard behaviour of any EventSource. You do not need to reconnect by hand.
The stream does not replay what you missed. Events carry no id: line, and a reconnect that sends
Last-Event-ID gets no backlog. Anything that changed while you were disconnected is not resent, so
refetch the state you show after every reconnect — the open event fires each time the connection
is made, and is the place to do it.
The server may also close the stream itself, for example when your access changes. The same reconnect then brings you back with your current access.
Example — subscribe and filter by entity
Paste this into the browser console on a page of the Jaah console, then edit a task in another tab. Each change to a task prints one line.
const stream = new EventSource('/api/stream')
const wanted = new Set(['task']) // the entities to act on; see the event catalog
stream.addEventListener('open', () => {
// Connected or reconnected: nothing is replayed, so refetch what you display here.
console.log('event stream open')
})
stream.addEventListener('message', (message) => {
const event = JSON.parse(message.data) // { entity, id, action }
if (!wanted.has(event.entity)) return
console.log(`${event.entity} ${event.id}: ${event.action}`)
// Refetch that item here, for example the task with this id.
})
stream.addEventListener('error', () => {
// A dropped connection. EventSource retries on its own after one second.
console.log('event stream interrupted, reconnecting')
})
// Stop listening: stream.close()
Editing a task's title prints a line such as task 4916: updated.