HighX
Server

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 dependency

That 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 dependency

A point read only wakes on writes to that one row.

Calling one

FromHow
ReactuseQuery(api.notes.recent, { color }) — live
Any clientclient.subscribe(path, args, onValue) — live
HTTPPOST /api/notes/recent through your own route — one-shot
Server rendercreateServerReader() for a hydration payload — see SSR

Next

On this page