Skip to content

Schema YAML format

A schema file describes your tables. The generator turns it into Dart models (see Generated code) and SQL migrations (see Migrations API). This page lists everything the parser accepts. Anything else is rejected with an unknown_key diagnostic.

Rule Detail
File name Must end in .gisila.yaml or .gisila.yml.
Location dart run gisila_orm:generate scans lib/, test/ and example/ recursively.
Skipped Anything under a .gisila/ directory and any file named project.gisila.yaml (the generator’s own snapshot).
Multiple files All discovered files merge into one project schema, so models and enums can reference each other across files.

The top level is a map. Each key is either the reserved key enums or a model name.

enums: # optional
TeamRole: [viewer, developer, admin]
TeamMember: # a model: one table
db_table: team_members # optional
columns: # required
role:
type: TeamRole
default: developer
is_null: false
indexes: # optional
idx_team_member_role:
columns: [role]
checks: # optional
role_not_owner:
expression: "role <> 'owner'"
Rule Detail
Pattern ^[A-Za-z_][A-Za-z0-9_]*$ (invalid_model_name otherwise).
Convention PascalCase. A name starting with a lowercase letter raises a naming_convention warning.
Uniqueness A model may be declared once per project (duplicate_model). A name can’t be both a model and an enum (enum_model_collision).
Key Type Required Default Description
columns map yes – Column name → column definition. Missing gives missing_columns.
db_table string no plural snake_case of the model name SQL table name. Must match ^[a-z_][a-z0-9_]*$. A PostgreSQL reserved word (for example user, order) gives reserved_table_name.
indexes map no – Named explicit indexes. See Indexes.
checks map no – Named table-level CHECK constraints. See Checks.

The default table name is the model name in snake_case, pluralised with simple English rules: User → users, Category → categories, Box → boxes, BlogPost → blog_posts.

If no column has is_primary: true, the parser inserts a first column:

id: { type: integer, is_primary: true, is_null: false, is_unique: true, is_index: true }

which the SQL emitter writes as "id" BIGSERIAL PRIMARY KEY. Any integer or bigint primary key, implicit or declared, becomes BIGSERIAL.

Column names must match ^[a-z_][a-z0-9_]*$. camelCase such as createdAt is rejected with invalid_column_name. Dart fields use the camelCase form (created_at → createdAt).

Key Type Default Applies to Description
type string – (required) all Column type. See Column types. Missing gives missing_type.
is_null bool true all false emits NOT NULL and makes the Dart field non-nullable and a required constructor parameter.
is_unique bool false all Emits UNIQUE. On a foreign key it also makes the inverse relation a has-one instead of a has-many.
is_index bool false all Emits CREATE INDEX "idx_<table>_<column>". Skipped for primary keys and unique columns (already indexed). On a vector column, builds a pgvector index (see Vector options).
is_primary bool false non-relation columns Marks the primary key and suppresses the implicit id. Must not be combined with is_null: true. Relationship columns can’t be primary keys.
allow_blank bool true varchar, text, uuid false adds a check to the generated validate() method that rejects empty or whitespace-only strings. No SQL is emitted.
default string, number, bool or null none all SQL DEFAULT. See Defaults.
max_length positive int 255 varchar, varchar[] Emits VARCHAR(n). Any other type gives invalid_max_length.
check string none all Column CHECK constraint, emitted as CONSTRAINT "<table>_<column>_check" CHECK (<expr>). The expression is copied into the SQL verbatim.
references string the type value relations Target model name. Required with type: foreign_key.
reverse_name identifier none relations Name of the inverse relation generated on the target model.
many_to_many bool false type: <Model> Makes the column a many-to-many relation backed by a junction table.
on_delete string SET NULL foreign keys NO ACTION, RESTRICT, CASCADE, SET NULL or SET DEFAULT (case-insensitive).
on_update string CASCADE foreign keys Same values as on_delete.
dimensions positive int – (required for vectors) vector Vector length: VECTOR(n).
index_method string hnsw vector hnsw or ivfflat. Used when is_index: true.
distance string l2 vector l2, cosine or ip (also innerProduct). Picks the index opclass.

Changing is_unique or is_index on a column that already exists isn’t migrated automatically; see what the differ detects.

Booleans must be real YAML booleans (true/false). A quoted "true" gives invalid_value.

Type names are case-insensitive for built-ins.

YAML type PostgreSQL Dart field Notes
varchar VARCHAR(255) or VARCHAR(n) String max_length sets n.
text TEXT String
integer INTEGER int BIGSERIAL when primary key.
bigint BIGINT int BIGSERIAL when primary key.
boolean BOOLEAN bool
date DATE DateTime
timestamp TIMESTAMP WITH TIME ZONE DateTime
decimal DECIMAL String Kept as exact text so money isn’t rounded. Parse it with a decimal package.
json JSONB Object Any JSON value. Written through encodeJsonColumn.
uuid UUID String
vector VECTOR(n) Vector Needs the pgvector extension and dimensions.
point POINT Point
box BOX Box
circle CIRCLE Circle
lseg LSEG Lseg
<element>[] <element type>[] List<T> See Arrays.
<EnumName> "<enum_snake_name>" the generated Dart enum Must be declared under enums:.
<ModelName> FK column typed like the target’s primary key the target PK’s Dart type A belongs-to relation, or many-to-many with many_to_many: true.
foreign_key (or ForeignKey) FK column typed like the target’s primary key the target PK’s Dart type Requires references:. Can’t be combined with many_to_many.

A PascalCase type that isn’t a built-in or an enum is treated as a model reference. Anything else is unknown_type, with a “did you mean” hint.

tags:
type: varchar[]
max_length: 64 # element type becomes VARCHAR(64)
default: '{}'
Rule Detail
Allowed elements varchar, text, integer, bigint, boolean, date, timestamp, decimal, uuid
Not allowed json[], vector[], geometric arrays, enum arrays, nested arrays, relations (invalid_array_element / invalid_relationship)
Dart element types String for varchar/text/uuid/decimal, int for integer/bigint, bool, DateTime for date/timestamp
Default A string array literal such as '{}' or '{a,b}', emitted as '{a,b}'::varchar[]
enums:
ReviewVerdict:
- pending
- approved
- rejected
Review:
columns:
verdict:
type: ReviewVerdict
default: pending
is_null: false
Rule Detail
Enum name Identifier; PascalCase recommended. Postgres type name is snake_case (ReviewVerdict → review_verdict).
Values Non-empty list of unique identifiers (invalid_enum_values, invalid_enum_value, duplicate_enum_value).
Default Must be one of the values (invalid_enum_default); emitted as 'pending'::review_verdict.
Across files The same enum may appear in several files only with identical values (duplicate_enum).
Post:
columns:
author: # belongs-to; stored as author_id
type: Author
reverse_name: posts # generates Author.posts
on_delete: CASCADE
editor_id: # foreign_key style; stored as editor_id
type: foreign_key
references: User
is_null: true
tags: # many-to-many through a junction table
type: Tag
many_to_many: true
reverse_name: posts
Behaviour Detail
Physical column <name>_id, unless the name already ends in _id.
Column type The target’s actual primary key type. An integer primary key gives BIGINT, a uuid primary key gives UUID.
Constraint Added after all tables: ALTER TABLE ... ADD CONSTRAINT "<table>_<column>_fkey" FOREIGN KEY ... ON DELETE <on_delete> ON UPDATE <on_update>.
Dart field camelCase of the physical column (author → authorId).
Relation accessor camelCase of the YAML name without a trailing _id (author → Post.author, editor_id → Post.editor).
Inverse Only generated when reverse_name is set. Has-many by default, has-one when the FK is is_unique: true.
reverse_name rules Must not collide with a column on the target, and two relations can’t use the same reverse_name on the same target (reverse_name_collision).
Unknown target unknown_reference, with a “did you mean” hint.

Many-to-many columns produce no column on either table. The generator creates one junction table per pair, named from the two table names sorted and joined with _ (books + users → books_users):

CREATE TABLE "books_users" (
"books_id" BIGINT NOT NULL,
"users_id" BIGINT NOT NULL,
"created_at" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ("books_id", "users_id"),
FOREIGN KEY ("books_id") REFERENCES "books" ("id") ON DELETE CASCADE,
FOREIGN KEY ("users_id") REFERENCES "users" ("id") ON DELETE CASCADE
);
Document:
columns:
embedding:
type: vector
dimensions: 1536
is_index: true
index_method: hnsw # or ivfflat
distance: cosine # l2 | cosine | ip

With is_index: true this emits CREATE INDEX "idx_documents_embedding" ON "documents" USING hnsw ("embedding" vector_cosine_ops);. Any schema with a vector column or vector index starts its up migration with CREATE EXTENSION IF NOT EXISTS vector;. Using dimensions, index_method or distance on a non-vector column gives invalid_vector_option.

default accepts a string, number, boolean or null. Lists and maps are rejected.

Column type Accepted Emitted SQL
any a function call such as gen_random_uuid(), NOW(), nextval('seq'), myschema.next_id(), optionally with a ::type cast verbatim
boolean true / false TRUE / FALSE
integer, bigint whole number the number
date, timestamp now, NOW(), CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME (any case) the function; now becomes NOW()
date, timestamp any other string quoted literal
varchar, text, uuid, decimal, json any other value quoted literal, with ' doubled
enum one of the enum values 'value'::enum_type
array '{}' or '{a,b}' '{a,b}'::varchar[] (cast per element type)
geometric text form such as '(1,2)' '(1,2)'::point

An invalid combination (for example default: abc on an integer) fails SQL generation with invalid_default, naming the column.

Review:
columns:
book: { type: Book }
reviewer: { type: User }
embedding: { type: vector, dimensions: 384 }
indexes:
idx_review_book_reviewer:
columns: [book, reviewer] # logical names; FKs become book_id, reviewer_id
unique: true
idx_review_embedding:
columns: [embedding]
using: ivfflat
distance: l2
Key Type Default Description
columns list of column names – (required) Logical YAML names. Foreign keys are rewritten to their _id column. Unknown names give unknown_column.
unique bool false CREATE UNIQUE INDEX. Not allowed with using (invalid_vector_index).
using string none hnsw or ivfflat: a pgvector index. Needs exactly one column; a multi-column vector index emits no SQL.
distance string the column’s distance, else l2 Opclass for a pgvector index.

The index key is used as the SQL index name.

Order:
columns:
total: { type: integer, is_null: false }
discount: { type: integer, is_null: false, default: 0 }
checks:
discount_below_total:
expression: "discount <= total"
Key Type Description
expression non-empty string SQL boolean expression, copied verbatim. Emitted as ALTER TABLE ... ADD CONSTRAINT "<check name>" CHECK (<expression>) after the tables.

The parser collects every problem before failing and throws a single SchemaValidationException. dart run gisila_orm:generate prints them in this form and exits with code 1:

error[unknown_type]: unknown column type "varchars"
--> lib/models/blog.gisila.yaml:5:13
|
5 | type: varchars
| ^^^^^^^^ did you mean "varchar"?
Code Cause
invalid_yaml, duplicate_key, empty_schema, expected_map, expected_list File isn’t a valid schema shape.
invalid_model_name, duplicate_model, naming_convention, enum_model_collision Model naming.
missing_columns, invalid_db_table, reserved_table_name Model keys.
invalid_column_name, duplicate_column, missing_type, invalid_type, unknown_type, unknown_key, invalid_key, invalid_value Column shape.
invalid_primary_key, invalid_max_length, invalid_check, invalid_default Column constraints.
invalid_relationship, missing_references, invalid_references, unknown_reference, invalid_referential_action, invalid_reverse_name, reverse_name_collision Relations.
invalid_enum_name, duplicate_enum, invalid_enum_values, invalid_enum_value, duplicate_enum_value, invalid_enum_default Enums.
invalid_array_element Arrays.
missing_dimensions, invalid_dimensions, invalid_index_method, invalid_distance, invalid_vector_option, invalid_vector_index pgvector.
invalid_index_name, unknown_column Indexes.
invalid_check_name Checks.

See also: Define a schema, Schema-first design, Generated code.