Skip to main content
Live Queries extend the Query Engine with a WebSocket push model. Instead of polling client.query(), you subscribe once — the server re-executes the query and pushes a new result every time a matching CRDT changes.

How it works

  1. Client sends SubscribeQuery over the existing WebSocket connection with a query spec and a query_id.
  2. Server executes the query immediately and pushes a first QueryResult frame.
  3. Whenever a CRDT in the namespace changes, the server checks all active live query subscriptions. Queries whose from glob matches the changed CRDT (and whose optional type filter matches) are re-executed, and a new QueryResult is pushed.
  4. Client sends UnsubscribeQuery to cancel. The server also cleans up automatically on disconnect.
The connection uses the same WebSocket established by the SDK — no additional socket is opened.

SDK — client.liveQuery()

LiveQueryHandle

LiveQueryResult


React — useLiveQuery()

The hook subscribes on mount, updates data on every push, and unsubscribes on unmount. loading is true until the first result arrives.

Supported query specs

Live Queries accept the same spec as client.query(): See the aggregation table for supported operations per CRDT type.

Performance notes

Type filter skips unrelated deltas. When you set type: "gcounter", the server skips re-execution entirely when an ORSet or LwwRegister changes — even if the glob pattern would match the key. Always set type when your pattern is cross-type.
Reconnect resilience. The SDK re-sends all active SubscribeQuery frames automatically after a WebSocket reconnect. No manual re-subscription is needed. Multiple handles per spec. Each liveQuery() call creates an independent subscription with its own query_id. If you need multiple listeners on the same query, use handle.onResult() multiple times on a single handle instead of calling liveQuery() twice.

Difference from useQuery