Skip to content

The codegen pipeline

dart run gisila_orm:generate is a single command that turns your schema files into everything the runtime needs. It writes three kinds of output, and it’s worth knowing which is which:

Output Example Role Edit it?
Dart bundle lib/models/blog.gisila.g.dart Models, <Model>Table, relations, query accessors Never; regenerated each run
Full-schema SQL blog.gisila.up.sql, .down.sql CREATE / DROP of the whole current schema Never; regenerated each run
Snapshot .gisila/schema_snapshots/project.gisila.yaml The schema as of the last run, used for diffing Never; commit it
Migrations lib/models/migrations/<millis>_initial_schema.up.sql, <millis>_auto_project_changes.up.sql What migrate up applies Review before applying; never after
  • After every edit to a *.gisila.yaml file. Commit the results together with the YAML.
  • In CI, run it and fail if git status shows changes, to catch a schema edited without regenerating.
  • To apply changes to a database. generate never connects to one; that’s migrate up.
  • With build_runner for multi-file schemas: the builder does nothing there. See build.yaml.
lib/**/*.gisila.yaml ─┐
test/**/*.gisila.yaml ├─▶ discover ─▶ merge + validate ─▶ emit Dart + full SQL
example/**/… ┘ │
▼
.gisila/…/project.gisila.yaml (previous snapshot)
│ diff (SchemaDiffer)
▼
migrations/<millis>_auto_project_changes.{up,down}.sql
│
▼
snapshot updated
  1. Discover schema files under lib/, test/ and example/, sorted by path.
  2. Merge and validate. Enums and model names are collected across all files first, so a model in post.gisila.yaml can reference Author in author.gisila.yaml. Any error stops the run before writing.
  3. Emit the bundle. With one schema file, beside it using its stem; with several, as lib/models/schema.gisila.*.
  4. Bootstrap migrations. If you have no migrations yet, write <millis>_initial_schema so a fresh database can be built from migrations alone.
  5. Diff. Compare the current schema with the snapshot. If they differ, write <millis>_auto_project_changes. If the change would lose data, write nothing, leave the snapshot alone and exit with an error until you re-run with --allow-destructive.
  6. Update the snapshot.

Incremental migrations are diffs between the snapshot and the current schema. The snapshot must be committed and shared: if a teammate’s checkout has no snapshot, their first run only records one and writes no migration, and the change you expected to see is lost. If two branches both change the schema, each generates its own migration; after merging, run generate again and check that the combined result is right.

*.gisila.up.sql always describes the whole current schema, so you can’t use it to upgrade an existing database. migrate skips these files when the directory also contains real migrations, and only falls back to them when there is nothing else (projects that never ran generate).

The emitted Dart depends only on package:gisila_orm/gisila.dart. It’s formatted with dart_style and contains no reflection; every model knows how to build itself from a row (fromRow) and turn itself back into one (toRow).

A schema change and what it produces:

# lib/models/blog.gisila.yaml - add a column to Post
slug: { type: varchar, is_null: true, is_unique: true }
Terminal window
$ dart run gisila_orm:generate
Generated 1 schema file(s) → lib/models/blog.gisila.g.dart, lib/models/blog.gisila.up.sql
Generated migration files:
Up: lib/models/migrations/1730460000000_auto_project_changes.up.sql
Down: lib/models/migrations/1730460000000_auto_project_changes.down.sql
Auto incremental migration generated: auto_project_changes
-- Migration: auto_project_changes
-- Generated on: 2026-11-01T10:00:00.000
BEGIN;
-- Add column: posts.slug
ALTER TABLE "posts" ADD COLUMN "slug" VARCHAR(255) UNIQUE;
COMMIT;