Schema field reference
Look up the fields used to define tables, indexes, relationships and validation.
Use this page to look up schema settings; start with the table name, ID and fields.
New to schema files? Follow the walkthrough.
- Check the table identityLook up namespace, table and primary_key.
- Check a column definitionFind its supported type and field options.
- Look up an optional ruleUse the index, relation or constraint section when needed.
- Register and inspect errorsCorrect the named definition before adding records.
Name the table
| Field | Required | Meaning |
|---|---|---|
namespace | Yes | Group name, such as shop. Use letter-prefixed snake_case. |
table | Yes | Table name, such as orders. Queries use shop.orders. |
primary_key | Yes | Array of column names that identify one record. |
version | No | Defaults to 1; increment as required by the schema-change workflow. |
SQL can address composite keys. The direct /rows/:schema/:pk read accepts only one key column.
Define columns
Repeat [[columns]] for each field.
| Field | Meaning |
|---|---|
name | Column name and the JSON key used in row writes. |
ty | Data type, chosen from the tables below. |
required | Defaults to false. Set true when the value must be supplied. |
Basic types
| Type | Use for |
|---|---|
str | Names, IDs and other text. |
i64 | Whole numbers that may be negative. |
u64 | Non-negative whole numbers. |
f64 | Approximate numeric values. |
bool | True/false values. |
bytes | Encoded binary values. |
Extended column types
| Type | Value or setting |
|---|---|
timestamp | Epoch milliseconds; supported input forms also include timestamp text. |
date | Epoch days; supported input forms also include date text. |
interval | Duration in milliseconds, including negative values. |
uuid | Valid UUID string. |
decimal | Exact decimal text, such as "19.99"; optional precision and scale rules. |
enum | String selected from declared variants. |
text | Text content. |
json | JSON object or array. |
list | Array; an optional element type checks each item. |
inet | IPv4 or IPv6 string. |
point | Latitude/longitude object or a longitude/latitude pair. |
vector | Vector metadata with [columns.params] and vector_dim. Write search values through the vector API. |
Picking the right type for common data
- Use
strfor ordinary identifiers anduuidwhen UUID validation is useful. - Use decimal strings or integer minor units for money. Avoid passing exact amounts through floating-point values.
- Include units in field names, such as
price_centsorduration_ms. - Check SQL return formats before converting dates or large numbers in your application.
Add an index
An index gives the query planner another way to find matching rows. Start with fields used by important filters and check the actual plan with EXPLAIN.
| Field | Meaning |
|---|---|
name | Index name used in plans and administration. |
columns | Ordered list of indexed columns. |
For [customer_id, placed_ms], a left-prefix match can use customer_id. Do not assume the same index helps a filter on placed_ms alone. Extra indexes also add write work.
Index examples
[[indexes]]
name = "by_status"
columns = ["status"] # single-column
[[indexes]]
name = "by_customer_and_time"
columns = ["customer_id", "placed_ms"] # composite, in declared orderDefine a graph relation
A relation names a connection between rows. Row writes maintain its derived graph links.
| Field | Meaning |
|---|---|
name | Relationship name used when following the link. |
from_col | Local column containing the destination ID. |
bidirectional | Defaults to true; also maintains a reverse link. |
target.namespace, target.table, target.pk | Destination table and primary-key column. |
Relation examples
[[relations]]
name = "supplied_by" # the verb you'll walk in code
from_col = "supplier_id" # the FK column on THIS table
bidirectional = true # default; reverse edge written atomically
[relations.target]
namespace = "shop" # target table's namespace
table = "suppliers" # target table's name
pk = "id" # target table's primary-key column
# Self-relations work - direction tags resolve the collision:
[[relations]]
name = "follows"
from_col = "followee_id"
bidirectional = true
[relations.target]
namespace = "social"
table = "users"
pk = "id"A relation does not require the destination row to exist. Add a foreign key when that rule is needed. See graph setup for key and traversal limits.
Require a matching row
A foreign key rejects a reference to a missing record.
| Field | Meaning |
|---|---|
name | Optional error-reporting name; defaults to the source column plus _fk. |
from_col | Local referencing column. |
target_schema | Destination namespace.table. |
target_col | Destination primary-key or unique-indexed column. |
on_delete | no_action, restrict, set_null, cascade or set_default. |
on_update | no_action, restrict or set_null; update cascades and defaults are not supported. |
Foreign-key example
[[foreign_keys]]
name = "fk_customer" # optional - defaults to "<from_col>_fk"
from_col = "customer_id" # column on THIS table
target_schema = "shop.customers" # "namespace.table" of the target
target_col = "id" # PK or unique-indexed column on target
on_delete = "no_action" # refuse deletion while referencedno_action and restrict refuse a delete while references remain. set_null requires a nullable source column. cascade deletes referencing rows recursively; a conflicting restriction or the cascade row budget rejects the whole delete. set_default substitutes the column default, or NULL when no default exists, and validates that replacement against the foreign key.
These propagation actions apply to deletes. cascade and set_default remain unsupported for on_update. Check cross-shard restrictions before linking separately placed tables.
Validate values with CHECK
A check uses name and expression to reject values that violate a rule, such as a negative quantity.
Expression grammar
| Operation | Example |
|---|---|
| Comparison | qty > 0 using =, !=, <, <=, > or >=. |
| Combine conditions | price > 0 AND price < 1000000; also OR and NOT. |
| Test missing values | description IS NOT NULL. |
| Allow a set | status IN ('pending', 'paid'). |
A NULL result fails the check. To allow a missing value, include it explicitly: qty IS NULL OR qty > 0.
CHECK examples
[[check_constraints]]
name = "qty_positive"
expression = "qty > 0"
[[check_constraints]]
name = "country_or_continent_set"
expression = "country IS NOT NULL OR continent IS NOT NULL"
[[check_constraints]]
name = "status_in_set"
expression = "status IN ('pending', 'paid', 'cancelled')"Extract a nested JSON value
Use [[extractions]] to derive a queryable column from a nested path at write time.
| Field | Meaning |
|---|---|
name | Name of the derived column. |
path | Dotted source path, such as customer.address.country. |
ty | Type of the extracted value. |
Missing paths produce the type's zero value. Extractions do not create full-text or vector indexes.
Extraction examples
# Pull a derived column out of a nested JSON row at write time.
# The dotted path walks into the JSON; missing fields emit zero values.
[[extractions]]
name = "customer_country"
path = "customer.address.country"
ty = "str"
[[extractions]]
name = "order_total_cents"
path = "totals.grand_cents"
ty = "i64"Set row expiry
TTL means time to live. Choose one way to determine when a row expires.
Two ways to declare it
| Setting | Behaviour |
|---|---|
Column ttl = "30d" | Gives the whole row a fixed lifetime from its write. Units: s, m, h, d, w. |
[ttl] with column = "expires_at" | Uses the row's timestamp or date value as its expiry. |
TOML expiry examples
# Row expiry (TTL) - two ways to declare it. Use ONE.
# (A) Fixed lifetime: put `ttl` on any column and the WHOLE row expires
# this long after it is written. Units: s / m / h / d / w.
[[columns]]
name = "id"
ty = "str"
required = true
ttl = "30d" # every row lives 30 days from write
# (B) Per-row expiry: instead of (A), add a [ttl] block pointing at a
# timestamp or date column. Each row expires at its own stored value.
#
# [ttl]
# column = "expires_at" # a `timestamp` or `date` columnSQL expiry example
CREATE TABLE app.sessions (
id TEXT NOT NULL PRIMARY KEY,
user_id TEXT
) WITH (ttl = '30d');- TTL applies to the whole row, not a single field.
- Existing rows are not assigned a new expiry until rewritten.
- A missing per-row expiry does not expire the record.
- Coordinate lifetimes across related tables; do not assume TTL cascades to all related records. Check configured foreign-key delete actions.
Describe a vector collection
[vector] describes how embeddings in the collection should be shaped. It does not generate them or index ordinary row writes.
| Field | Required | Meaning |
|---|---|---|
dim | Yes | Positive number of values in each vector. |
distance | No | cosine by default; also l2, dot, manhattan or l1. |
embedder | No | Model identity. A different supplied identity is rejected; an omitted identity is not blocked. |
Vector metadata example
# Optional. One [vector] table per manifest.
[vector]
dim = 768 # REQUIRED. No default.
distance = "cosine" # the default. Five values accepted.
embedder = "your-768d-model" # replace with the model used for these vectors
# A `vector` COLUMN carries the same width in its own params block.
# Declare both and the two dims must agree, or registration fails.
[[columns]]
name = "embedding"
ty = "vector"
[columns.params]
vector_dim = 768How distance resolves at runtime
An omitted request measure uses the declared distance. A conflicting non-default declaration returns 400. A cosine declaration does not reject another request measure in this contract, so enforce consistent settings in your application.
Use [vector], not [[vectors]]; use schema distance, not request metric. Unknown manifest fields can be ignored, so verify the stored definition after registration. IVF and quantization settings use separate endpoints.
Complete reference example
This example combines columns, indexes, relations, foreign keys and checks. Register its target tables before using it.
Combined orders schema
# A schema using every block at once.
namespace = "shop"
table = "orders"
primary_key = ["id"]
[[columns]]
name = "id"
ty = "str"
required = true
[[columns]]
name = "customer_id"
ty = "str"
required = true
[[columns]]
name = "product_id"
ty = "str"
required = true
[[columns]]
name = "qty"
ty = "i64"
[[columns]]
name = "total_cents"
ty = "i64"
[[columns]]
name = "placed_ms"
ty = "u64"
[[indexes]]
name = "by_customer"
columns = ["customer_id"]
[[indexes]]
name = "by_product_and_time"
columns = ["product_id", "placed_ms"]
[[relations]]
name = "bought_product"
from_col = "product_id"
bidirectional = true
[relations.target]
namespace = "shop"
table = "products"
pk = "id"
[[relations]]
name = "by_customer"
from_col = "customer_id"
bidirectional = true
[relations.target]
namespace = "shop"
table = "customers"
pk = "id"
[[foreign_keys]]
name = "fk_customer"
from_col = "customer_id"
target_schema = "shop.customers"
target_col = "id"
on_delete = "restrict"
[[check_constraints]]
name = "qty_positive"
expression = "qty > 0"
[[check_constraints]]
name = "total_positive"
expression = "total_cents > 0"