Realtime

Subscribe to a collection and get every create, update and delete as it happens. Each event is filtered through the subscriber's own rules, so nobody hears about a row they couldn't fetch.

Server-sent events

One request with the browser’s built-in EventSource:

app.ts
const events = new EventSource("/api/realtime?subscribe=messages,notices");
events.addEventListener("record", (e) => {
  const { action, collection, record } = JSON.parse(e.data);
  // action: "create", "update" or "delete"
});

Omit subscribe to get every collection the caller can read. The first event is connected and lists the subscriptions. EventSource can’t send headers, so a signed-in page passes its token in the URL: &token=….

A WebSocket

sluurp.socket() in the browser client does the same over a single WebSocket (/api/ws), plus presence and ephemeral messages:

app.ts
const socket = sluurp.socket({ subscribe: ["messages"] });
socket.on("record", ({ action, record }) => show(record));

socket.join("room:4b");                         // presence
socket.emit("room:4b", "typing", { by: me });  // broadcast, not stored
socket.send("messages", { body: "Hello" });     // a write, same rules as over HTTP

It reconnects automatically and rejoins its topics.

What it costs

Subscribers are grouped before anything is read. A rule that doesn’t reference the caller gives everyone the same result, so it runs once for all of them: ten thousand anonymous subscribers cost one query. Only a rule like author = @request.auth.id runs per user, and connections from the same user share it.

Usually you want Sync instead

Most screens don’t need raw events; they need a list that stays up to date. Sync keeps a filtered collection current in the browser from the same events, and live views do it for server-rendered HTML.