Zonai is a Dart backend-as-a-service framework that turns schema definitions into a complete REST API. Write your tables, rules, and business logic in Dart — Zonai handles the HTTP layer, the database, and the auth system.
It is designed for Dart and Flutter developers who want to build a production-quality backend without wiring together boilerplate.
Go from dart create to a running server with auth and CRUD in about ten minutes.
client.db.listen and /db/stream* push updates as SQLite changes. Never poll.
zonai_client wraps auth, db, photos and email so apps never hand-roll HTTP.
Write a table, get an API#
Define a table in Dart:
final class TaskTable extends Table<Task> {
TaskTable(super.$)
: id = $.id('id', (s) => s.id, fromString: TasksId.new, generate: TasksId.generate),
title = $.text('title', (s) => s.title),
isComplete = $.boolean('is_complete', (s) => s.isComplete),
createdAt = $.createdAt('created_at', (s) => s.createdAt),
updatedAt = $.updatedAt('updated_at', (s) => s.updatedAt);
// …
}
final tasks = table('tasks', TaskTable.new);
Every endpoint below exists the moment that file does — no handlers, no codegen step:
POST /db create GET /db/stream live single row
GET /db read GET /db/stream/list live list
PATCH /db update GET /db/stream/count live count
DELETE /db delete
GET /db/list list POST /auth/sign-up from an AuthTable
GET /db/count count POST /auth/sign-in
And live UI is a subscription, not a timer:
client.db.listen
.list(body: StreamListBody(table: 'tasks'), fromJson: Task.fromJson)
.listen((tasks) => setState(() => _tasks = tasks));
Live UI does not need polling. Every table gets GET /db/stream, /db/stream/list, and /db/stream/count. In Dart use zonai_client's client.db.listen. Search these docs for stream / listen — not "realtime", "SSE", "socket", or "EventSource". Full guide: Live Queries (Streaming).
What You Get Out of the Box#
A full REST API for every table — create, read, update, delete, list, count, and live stream endpoints are auto-handled from your schema. No HTTP handler code, no generation step needed.
Built-in authentication — password sign-up/sign-in, one-time passcodes, and magic links are available by mixing a single trait into an auth table. Sessions, refresh, and logout are included.
Authorization rules — evaluated before any SQL executes. Return true or
false; a denied request gets a 403 immediately, with zero database access.
Generated Dart client — zonai_client wraps auth, admin auth, db (including
db.listen streams), photos, and email so apps do not hand-roll HTTP.
Transactional email via SMTP — HTML templates with Mustache variables, sent from lifecycle hooks.
Scheduled background jobs — cron-syntax jobs compiled into a separate worker with access to the full database API.
Per-IP rate limiting — configurable per-table and per-operation with a simple policy class.
Project-linked binary — zonai build produces build/zonai with your ops/rules linked in-process for the CRUD hot path.
How It Works#
Your Dart code compiles into a project-linked server binary (operations and rules in-process) plus workers for config, extensions, rate limits, and crons. Each HTTP request passes through an ordered pipeline:
HTTP Request
→ Rate Limit (worker)
→ Rules (in-process)
→ Operations (in-process)
→ SQLite (execution)
→ Extensions (worker)
→ Response
Nothing runs interpreted at request time on the AOT path. All logic is compiled Dart. See How a Request is Processed for the full walkthrough.
Hot-reload development — worker sources are watched and recompiled automatically. Restart
serve after editing ops/rules so the linked project entry reloads.
Browse the Docs#
Press ⌘K to search every page, or start from a section:
Install the CLI and get a server answering requests.
The model behind the framework — read this before going deep.
Project settings, runtime config and secrets.
Define tables in Dart, then turn schema changes into SQL.
The HTTP surface every table gets for free — including live streams.
Deny-by-default checks that run before any SQL executes.
Password, OTP and magic-link sign-in, sessions and admins.
Run your own Dart before and after each operation.
Cron-scheduled work with full database access.
Transactional email over SMTP, with Mustache templates.
Per-IP limits, applied before rules run.
zonai_client — a typed client so apps never hand-roll HTTP.
Every zonai command and flag.
Ship the binary and keep it running.
What Zonai Is Not#
-
Not a full application framework — Zonai is an API server (no HTML rendering). Use
zonai_client(or raw HTTP) from Flutter/Dart apps - Not a managed cloud service — you host it yourself, anywhere that runs a Linux/macOS/Windows binary
- Not a general-purpose ORM — it is opinionated about how APIs are structured and uses SQLite as its database
-
Not "poll-only" for live UI — use
/db/stream*/client.db.listen(search docs for stream, not "realtime"/"SSE")
For LLMs and coding agents#
A curated docs index lives at /llms.txt. Inside a Zonai app, run zonai ai
to install project-local assistant rules (Cursor, Claude, Copilot, etc.).
Next Steps#
- Installation — prerequisites and CLI setup
- Quick Start — create and run your first project
- Project Structure — understand the directory layout
-
Live Queries (Streaming) —
client.db.listen//db/stream*(do not poll)
