A schema, in 15 lines
Here is a schema that declares two tables —customers and orders — with a foreign key from orders back to customers:
schema.json
CREATE TABLE, no migration file, no SQL dialect to learn. When a block or an API call writes a record to customers, the runtime checks the value against the declared field shapes — wrong types are rejected, missing required fields are rejected, duplicate email values are rejected — before anything reaches the database.
Why JSON, not SQL
The schema is a JSON object, not a SQL DDL script, for two reasons. It lives on the same document the page does. Pages and schema travel together. When youGET a page document, you get the schema with it. When you PATCH the document to add a phone field on customers, the page document and the schema update in one transaction.
The Builder can edit it. A non-technical owner uses the drag-drop schema designer to add a field; the designer writes JSON. A developer integrating from the API edits the same JSON. Both reach the same artifact through different surfaces.
The trade-off is that the schema is narrower than full SQL. Composite indexes, partial indexes, triggers, and stored procedures aren’t expressible. For the apps gavAI is built for — internal tools, customer portals, signup flows — the JSON shape covers what you need.
What goes in a schema
A schema is an object keyed by table name. Each table hasfields, and each field has a type plus optional constraints.
The full constraint vocabulary on a field, for quick reference:
You define tables and fields in the Builder’s schema designer (point-and-click), or by editing the JSON document directly through the API. Both surfaces produce the same artifact.
How blocks bind to schema
Blocks that read or write data bind to schema tables by name. The runtime resolves the binding when the page renders.- A Form block specifies a
schema_table— when a visitor submits, the runtime writes a record to that table. - A Table block fetches all records from the named table, with pagination, filtering, and sorting built in.
- A DetailPanel reads a single record from the named table and renders it in read mode or edit mode, gated by user permissions.
Form block pointing at a non-existent table is rejected before the page deploys — you don’t ship broken pages.
See the block reference for the binding options each block exposes.
How capabilities bind to schema
TherunQuery capability executes typed queries against schema-defined tables. You pass a table name and filter criteria; the runtime returns records validated against the declared field shapes.
This is the primary way blocks and agent workflows read data at runtime. Where a Table block is enough for a flat list, runQuery is the answer when you need a custom filter, a join, or a programmatic read from inside a workflow.