Skip to content

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.