Queries
Reads that stay current. The core of the framework.
A query reads tenant data. Subscribe to it from a browser and it stays current for as long as the component is mounted.
import { query } from "@highx/core";
export const recentNotes = query({
name: "notes.recent", // the wire path, mirrored in the generated api
args: colorArgs, // validated before the handler runs
returns: noteRows, // validated before the value goes back
auth: can("notes:read"), // required — see Authorization
handler: (ctx, args) =>
ctx.db.query("notes").withIndex("byColor", { color: args.color }).order("desc").take(20),
});The rules
A query is read-only and deterministic. It cannot write, cannot fetch, and
cannot read the clock — ctx.now() does not exist here. That is not
restrictiveness for its own sake: it is what makes a query safe for the engine
to re-run on your behalf whenever its data changes.
The query builder
ctx.db.query("notes")
.withIndex("byColor", { color }) // equality on the index's leading fields
.range({ gte: since }) // bounds the next field along
.order("desc")
.take(20); // or .collect(), or .first()| Method | |
|---|---|
withIndex(name, prefix?) | Choose a declared index and pin its leading fields |
range(bounds) | gt, gte, lt, lte on the field after the prefix |
order("asc" | "desc") | Index order, or its reverse |
take(n) | At most n rows |
collect() | Every matching row |
first() | The first row, or undefined |
Unsupported shapes throw rather than guessing: an unknown index, prefix fields out of order, two ranges, or ranging past the end of the index.
withIndex("byColor", { color }) is not just a filter — it is the
subscription's address. A write to a different colour never wakes this
query. See reactivity.
Subscribable queries return rows
The engine builds patches by diffing results row by row, so a subscribable query must return an array of rows.
A query returning a scalar or a single object still works over HTTP, but cannot be subscribed to. Compute aggregates in the component from rows you already have.
Avoid unindexed scans
ctx.db.table("notes").list(); // every row, and a whole-table dependencyThat records a dependency on the entire table, so every write to it wakes the subscription. Measured at 27–31× slower than an indexed read once there are subscribers. → Limits
Reading a single row
const note = ctx.db.table("notes").get(id); // records a document dependencyA point read only wakes on writes to that one row.
Calling one
| From | How |
|---|---|
| React | useQuery(api.notes.recent, { color }) — live |
| Any client | client.subscribe(path, args, onValue) — live |
| HTTP | POST /api/notes/recent through your own route — one-shot |
| Server render | createServerReader() for a hydration payload — see SSR |