FK + CHECK constraints.
Foreign keys and CHECK constraints are declared in the TOML
manifest and enforced at write time. Foreign keys ship with three on-delete
actions — no_action, restrict and set_null — and CHECK follows SQL’s 3-valued
logic. A violation comes back as HTTP 409 with a typed constraint_violation body: no silent skip, no partial commit.
Foreign keys.
# manifest.toml
namespace = "shop"
table = "orders"
primary_key = ["id"]
# ... [[columns]] declarations ...
[[foreign_keys]]
name = "fk_client"
from_col = "client_id"
target_schema = "shop.clients"
target_col = "id"
on_delete = "restrict" # no_action | restrict | set_null no_action shipped Reject the delete if any child row references the parent. Same semantic as 'restrict' here. restrict shipped Reject the delete with a typed FK error if any child row references the parent. set_null shipped Set the referencing column to NULL in every child row, then delete the parent. cascade deferred Recursively delete child rows. Roadmap. set_default deferred Set the referencing column to its declared default. Roadmap. CHECK expressions.
CHECK expressions support literals,
= != < <= > >=,
AND / OR / NOT,
IS [NOT] NULL, and
IN (..).
[[check_constraints]]
name = "amount_positive"
expression = "amount >= 0 AND status IN ('pending', 'paid', 'shipped')"
[[check_constraints]]
name = "refund_window"
expression = "refunded_at IS NULL OR refunded_at >= shipped_at" 3-valued logic.
CHECK uses the same semantic SQL has shipped for decades: any operand that
is NULL makes the whole expression NULL, and a NULL expression
passes the CHECK (it does not fail). That matches the
industry-standard 3-valued-logic behaviour. If you want NULL to fail the
CHECK, add an explicit IS NOT NULL guard.
Errors.
Constraint violations surface as typed HTTP errors:
409 Conflict with a
constraint_violation body containing
the constraint name, kind (foreign_key
or check), and the rejected row's
primary key. No silent skip, no partial commit.