The client query is only one part of the tree
import { project, relation, runQuery, where } from "@raquery/query";
const orders = project
whererelation"profile", "status == 'open'",
"name", "total", "status"
;
const result = await runQuery"/profile", orders, {: 42 };The client can filter and project orders, but it does not decide what orders means. On the server, that public name is itself a relational expression:
query = profile_catalog.(request.query) # bind public relations
query =(query, store_id) # compose authorization
plan =(query) # compile the whole treeprofile_catalog.apply replaces the public source with the application’s own joins, projections, and renames. enforce_store_scope adds another relational selection. The compiler never receives three disconnected concerns—it receives one tree.
That is the useful trick behind RAQ: client intent, the exposed data model, and server policy remain the same kind of thing, so they compose before execution.
What falls out of that model
- A catalog is a real boundary, not a list of tables the client may access.
- Authorization can constrain the query itself instead of being hidden in endpoint-specific handlers.
- The serialized AST can also be evaluated locally, inspected, canonicalized, batched, or conservatively reused.
- Generated clients describe public catalog relations without exposing physical database models.
RAQ stays close to relational algebra and SQL while giving applications control over the relational world a client is allowed to query.
Try the smallest complete example
The five-minute quick start builds a query, shows its serialized form, and evaluates it over local rows. Then run a server to see the same query cross a catalog boundary.