Catalogs
A catalog is not just a list of allowed table names. Each public relation is a server-owned relational expression that RAQ substitutes into the client query.
This page follows one relation from its definition to executed SQL.
Define the public relation
The repository’s profile catalog exposes customer order summaries. Its public relation is implemented by joining three physical sources:
from raq_lib.builders import attr, eq, join, project, rename, source
from raq_lib.catalog import Catalog, CatalogSurface
profile_catalog =({
"profile":(
query=(
(
(
(("orders"), {
"customer_id": "profile_customer_id",
"total": "total",
"store_id": "store_id",
"channel": "channel",
"status": "status",
}),
(("customers"), {
"id": "customer_id",
"customer_name": "name",
}),
(("profile_customer_id"),("customer_id")),
),
(("stores"), {
"id": "profile_store_id",
"name": "store_name",
}),
(("store_id"),("profile_store_id")),
),
["name", "total", "store_id", "channel", "status", "store_name"],
),
keys=[["name", "total", "store_id"]],
)
})Server-owned catalog definitions use expression builders for join predicates, so the boundary between identifiers and values remains explicit. String predicates remain available as frontend shorthand.
The client sees one relation named profile. It does not see orders, customers, or stores, and it does not reproduce their joins.
keys describes stable identity for schema generation and catalog-change materialization. It does not grant additional query access.
Query it from the client
The client composes over the public shape:
import { project, relation, runQuery, where } from "@raquery/query";
const openOrders = project
whererelation"profile", "status == 'open'",
"name", "total", "status", "store_name"
;
const result = await runQuery
"http://127.0.0.1:8001/profile",
openOrders,
{: 42 }
;The serialized query contains the public source—not the physical implementation:
{
"type": "project",
"input": {
"type": "select",
"input": { "type": "source", "name": "profile" },
"predicate": {
"type": "eq",
"left": { "type": "attr", "name": "status" },
"right": { "type": "value", "value": "open" }
}
},
"fields": {
"name": { "type": "attr", "name": "name" },
"total": { "type": "attr", "name": "total" },
"status": { "type": "attr", "name": "status" },
"store_name": { "type": "attr", "name": "store_name" }
}
}Bind the public source
On the server, Catalog.apply parses the request and replaces every public source with a deep copy of its catalog expression:
query = profile_catalog.(request.query)Conceptually, the bound tree becomes:
project name, total, status, store_name client
└─ select status == "open" client
└─ project public profile fields server catalog
└─ join store_id == profile_store_id server catalog
├─ join profile_customer_id == customer_id
│ ├─ rename physical orders
│ └─ rename physical customers
└─ rename physical storesThe client’s operations remain outside the server definition. There is no endpoint-specific translation into a different query model.
Compose authorization into the same tree
The profile endpoint scopes physical orders rows using trusted request context:
from raq_lib.rewrite import enforce_store_scope
def apply_profile_auth(query, context):
store_id = context.("store_id")
if store_id is None:
return query
return(
query,
store_id,
source_names={"orders"},
)After catalog binding, the rewrite can reach the physical orders source inside the server-owned expression and wrap it with store_id == 42. The client cannot remove that selection because it never controls the bound tree.
Compile and execute
The endpoint pipeline stays small because each concern produces another relational tree:
query = profile_catalog.(request.query)
query =(query, request.context)
plan =(query)
rows =(plan)
return {
"sql": plan.sql,
"params": plan.params,
"rows": rows,
}Values such as 42 and "open" become bound SQL parameters. The response keeps the generated SQL visible during the preview so the combined behavior remains inspectable.
What the boundary rejects
If the client requests a source the catalog does not expose:
relation"orders"binding fails with unknown source: orders. Physical source names are useful inside the server definition, but they are not automatically public.
The schema endpoint reflects the same boundary:
curl http://127.0.0.1:8001/schemaIt describes profile and its public fields. It does not publish the underlying three-table layout.
Next, read Schema code generation to turn that public contract into a typed client.