Typed client

Live queries

Subscribe to a query and get a fresh result after every commit that could change it.

Any select builder becomes a live query with .live(). Subscribers get the current result immediately, then a fresh result after any commit that could have changed it.

const live = db
  .selectFrom("orders")
  .select(["order_id", "status", "total"])
  .where("status", "=", "pending")
  .live();

const subscription = await live.subscribe({
  onChange(rows) {
    render(rows); // Array<{ order_id: number; status: string; total: number }>
  },
  onError(error) {
    console.error(error);
  },
});

// later
await subscription.close();

The rows are typed exactly as execute() would return them, and typeof live.$inferRow gives you that type without running anything.

As an async iterable

LiveQuery is also an AsyncIterable, which suits a for await loop or any consumer that prefers pull to push:

for await (const rows of live) {
  render(rows);
  if (done) break; // breaking closes the subscription
}

Breaking out of the loop closes the subscription immediately rather than waiting for the next change — worth knowing if you are cancelling a view that may sit idle for a long time.

What triggers a re-run

A subscription records the tables its query depends on. A commit touching a dependency re-runs the query; a commit that cannot affect it does not. Identical results are suppressed, so a re-run that produces the same rows does not call onChange.

Correctness never depends on a notification arriving. Each check reconciles against the current committed version in storage, so a missed or duplicated cross-tab hint cannot produce a stale result — it can only delay a fresh one.

Across tabs and workers

Live queries work the same whether the engine runs in this thread or in a worker. When several tabs share a database, commits in one tab reach subscriptions in another; see Workers & multi-tab for the coordination details and options.

Handlers

HandlerWhen
onChange(rows)The initial result, then every changed result.
onError(error)A re-execution failed. The subscription stays open.
onComplete()Once, when the subscription or its owning set closes.

On this page