realtime
every query() function is reactive by default. clients subscribe over a WebSocket; when the rows your query touched change, the server re-invokes the function and pushes the new result. no extra code on your side — write a normal SQL query and the platform handles the rest.
how it works
- subscribe — the client opens a WS to
wss://api.briven.tech/v1/projects/<id>/realtime, sends{ type: 'subscribe', function, args }, receives the initial result + a subscription id. - register — the platform invokes the query once, captures the list of tables it touched (e.g.
['posts', 'users']), and stores the subscription under those table keys. - notify — every
mutation()publishes the tables it wrote to via Postgres LISTEN/NOTIFY. the realtime service hears the NOTIFY and looks up every subscription that touched that table. - re-invoke + push — the platform re-runs each matching subscription with the original args, diffs against the last sent result, and pushes a
{ type: 'result' }frame only when the value changed. - unsubscribe — the client sends
{ type: 'unsubscribe', id }or just closes the socket. the server cleans up subscription state and UNLISTENs when no subscriber remains for a table.
touched-tables tracking
the runtime instruments ctx.dband records every table the query read from. the list is stored on the invocation envelope (visible in the dashboard logs tab as "touched") and used to register the subscription. you can't opt out — every table you read becomes a re-invoke trigger.
for very hot tables you don't actually want to subscribe to (e.g. an audit-log read inside an otherwise stable query), consider splitting that read into a separate action() call so it doesn't register the subscription.
when not to use it
realtime carries a cost — every active subscription holds an open WS and a server-side subscription record. for one-shot reads (e.g. a page that loads data once on navigation), call the function via HTTP invoke instead:
POST https://api.briven.tech/v1/projects/p_xxx/invoke
Authorization: Bearer brk_xxx
Content-Type: application/json
{ "function": "getStaticPage", "args": { "slug": "about" } }the SDK clients (useQuery et al) always subscribe — pass { reactive: false } to make them one-shot HTTP calls instead.
concurrency limits
concurrent subscription caps live on your tier. free: 100 concurrent. pro: 1,000. team: 10,000. the limit is per-project, not per-user — adjust your client's subscription pattern if many users will be online simultaneously.
debugging
- dashboard logs tab — filter by your query function name; the touched field shows which tables the platform registered.
- per-function stats — count + p50/p99 over the last 24h on the functions tab. high count + flat p99 = you have a chatty subscription.
- prometheus —
briven_realtime_active_subs+briven_realtime_active_channelsif you self-host; the operator grafana dashboard has both as graphs.
protocol reference
the SDK wraps this; you only need to know it if you're writing your own client. JSON-over-WS, no binary frames.
// → client
{ "type": "subscribe", "function": "listTodos", "args": { "filter": "open" } }
// ← server (initial)
{ "type": "result", "id": "sub_01HZ…", "data": [{ "id": "td_…", "body": "ship realtime" }] }
// ← server (push, fires whenever the result diffs)
{ "type": "result", "id": "sub_01HZ…", "data": [...] }
// → client (terminate)
{ "type": "unsubscribe", "id": "sub_01HZ…" }see also: functions · client sdks · http api