Read data
Use db.query.from for application queries. The builder is bound to the configured database and emits the portable query model; constructing it is synchronous and performs no network request.
Return entities
Section titled “Return entities”const openTasks = db.query .from(Task) .where(Query.not(Query.is(Task.status, "done"))) .orderBy(Task.createdAt, "desc")In the browser, an observed entity-focused query returns live entity handles. Read fields under .data; mutation methods and local pending state remain separate.
Select only the shape you need
Section titled “Select only the shape you need”const taskCards = db.query.from(Task).select({ id: Task.id, title: Task.title, status: Task.status,})Projection reduces synchronization-independent query work, component updates, MCP output, and server result size. Keep reusable shapes near the domain definition.
Follow relations
Section titled “Follow relations”const assignedTasks = db.query.from(Task).select({ id: Task.id, title: Task.title, assignee: Task.assignee .select({ id: User.id, name: User.name }) .optional, comments: Comment.task.reverse.select({ id: Comment.id, body: Comment.body, }),})Forward references follow a field. .reverse follows rows pointing back. Optional and many cardinalities remain explicit in the result type. Every related entity and field is independently filtered by policy.
Query traits
Section titled “Query traits”const assigned = db.query.from(Assignable).where({ assignee: me.id })A trait root returns every visible concrete composer. Use this for cross-type inboxes or generic tools. If the consumer immediately branches on every concrete type, separate entity queries may be clearer and cheaper.
Express filters
Section titled “Express filters”Use the object form for equality. Import Query from ramose/db for other predicates:
const urgent = db.query.from(Task).where( Query.not(Query.is(Task.status, "done")), Query.gte(Task.priority, 3), Query.startsWith(Task.title, "Ship", { ignoreCase: true }),)Use Query.build(function* (q) { … }) for relational clauses and aggregate projections. The callback receives the clause builder; Query.rule uses the same callback convention for reusable named rules.
Ask for one deliberately
Section titled “Ask for one deliberately”const project = organizationDb.query .from(Project) .where({ name }) .one()Use .one() for zero-or-one and .oneOrFail() for exactly one. Never rely on an unspecified first row.
Page stable ordered results
Section titled “Page stable ordered results”const firstPage = db.query .from(Task) .orderBy(Task.createdAt, "desc") .limit(50).after(null)The returned cursor is opaque and tied to principal, database, query, catalog, and order. Supply it to the same query for the next page. Use offset only for small, stable administrative lists.
Aggregate inside the database
Section titled “Aggregate inside the database”Count, sum, minimum, maximum, and grouped results are query projections. Aggregates see only visible facts and share the same query budget.
For frequently rendered dashboard totals, consider maintaining a summary entity through authoritative operations. That trades write work for predictable reads.
Time and history
Section titled “Time and history”The browser client observes the current local view. It does not expose asOf or history methods. See History and time for the engine’s temporal semantics and server query support.
Keep work bounded
Section titled “Keep work bounded”Filter on indexed selective fields early, project narrowly, bound every nested collection, page lists, and cap recursion. Query budget failures are useful feedback; raising a global limit is rarely the first fix.