Skip to main content
A gavAI app needs to store data — customers, orders, leads, whatever the app is about. The schema is how the app declares the shape of that data. It lives as a JSON object on the workspace’s app document, names the tables, and declares each field’s type and constraints. The runtime validates every write against this schema. By the end of this page you will know what the schema is, where it lives, what goes inside it, and how blocks and capabilities bind to it.

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
{
  "schema": {
    "customers": {
      "fields": {
        "id":    { "type": "uuid",   "primary": true },
        "name":  { "type": "string", "required": true },
        "email": { "type": "string", "required": true, "unique": true }
      }
    },
    "orders": {
      "fields": {
        "id":          { "type": "uuid",   "primary": true },
        "customer_id": { "type": "uuid",   "references": "customers.id" },
        "amount":      { "type": "number", "required": true },
        "status":      { "type": "string", "required": true }
      }
    }
  }
}
That is the whole declaration. No 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 you GET 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 has fields, 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.
Bindings are checked at page-publish time. A 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

The runQuery 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.