LogoZonai

Project Structure

What every directory and file in a Zonai project does.

A typical Zonai project looks like this:

my_app/
├── zonai.yaml                  # Project config — paths, server settings, build targets
├── pubspec.yaml
├── .env                        # Local secrets (gitignored)
├── .env.prod                   # Production secrets (gitignored)
├── lib/
│   └── src/
│       ├── schemas/            # Table definitions (one file per table)
│       ├── config/             # AppConfig — secrets, SMTP, JWT settings
│       ├── rules/              # Authorization rules
│       ├── operations/         # Custom SQL generation (optional)
│       ├── extensions/         # Lifecycle hooks
│       ├── rate_limit/         # Per-table throttling
│       ├── crons/              # Scheduled background jobs
│       └── email_templates/    # Mustache HTML email templates
├── .zonai/
│   ├── migrations/             # Generated SQL migration files (commit these)
│   ├── executables/            # Compiled worker binaries (gitignored)
│   └── data/                   # SQLite database and uploaded photos (gitignored)
└── build/                      # Production bundle — only after `zonai build`

Source Directories (lib/src/)#

These are the only directories you write code in.

schemas/ — one Dart file per table. Each file defines the row type, the table class, and calls table() or authTable() to register it. All files here are auto-discovered.

config/ — one or more Dart files exporting an AppConfig main() function. Contains JWT secret, SMTP configuration, base URL, and other runtime settings. Compiled into the config worker.

rules/ — authorization files. <table>_table_rules.dart controls operation-level access; <table>_row_rules.dart controls per-row access. Unoverridden operations default to denied.

operations/ — custom SQL generation. Optional — most tables don't need a file here. Defaults already cover CRUD and live streams (/db/stream*). Add a file to override SQL, add JWT claims to an auth table, or define non-standard operations. See Streaming.

extensions/ — lifecycle hooks that run before and after mutations and auth events. Optional — add a file when a table needs side effects (sending email, creating related rows, audit logging).

rate_limit/ — per-table throttle configuration. Optional — a default policy applies to any table without a file.

crons/ — scheduled background job classes. Each file defines a class extending CronJob. Optional.

email_templates/ — HTML files with Mustache variables used by email.send.custom(...). Built-in templates are auto-generated here on first use.

Generated Directories (.zonai/)#

Zonai manages these. Don't edit their contents by hand.

.zonai/migrations/ — SQL files generated by zonai db migrate generate. Commit these to version control — they are the source of truth for your schema history.

.zonai/executables/ — compiled worker binaries. Gitignore this directory. Rebuilt by zonai serve (auto) and zonai compile (manual).

.zonai/zonai — optional AOT project binary used when serve --release runs from the app root (dev/prod without shipping build/).

.dart_tool/zonai/ — generated entrypoints (project_main.dart, worker stubs). Regenerated on compile/serve.

.zonai/data/ — the SQLite database file and uploaded photo files. Gitignore this. Never commit database files or user uploads.

The build/ Directory#

Created by zonai build. Contains everything needed to run on a production server: the project-linked zonai binary (ops/rules in-process + full CLI), worker executables, migration files, email templates, and zonai.yaml. Ship this entire directory to your server.

Naming Conventions#

Files in each worker directory are auto-discovered by convention:

TypeFilename PatternExample
Table rules <table>_table_rules.dart task_table_rules.dart
Row rules <table>_row_rules.dart task_row_rules.dart
Operations <table>_operations.dart user_operations.dart
Extensions <table>_extensions.dart item_extensions.dart
Rate limits <table>_rate_limits.dart item_rate_limits.dart

No registration is needed — any .dart file in the correct directory is automatically included.

Live queries ship with every table. Clients should use zonai_client db.listen or GET /db/stream* instead of polling. Details: Streaming (Live Queries).