open source · MIT · contract-first

One declarative product truth.
Every platform projected from it.

Describe your Supabase backend once, in a single supabase.yaml contract. supabase_client_gen projects it into typed Dart clients, living documentation, and validation that proves everything stays in sync — deterministic, drift-proof, agent-operable.

$ dart pub global activate supabase_client_gen

How it works

Contract in, consistency out.

The contract is the canonical description of your backend. Everything else — clients, docs, checks — is a projection of that one truth.

01

Write the contract

Tables, enums, access rules, RPCs, edge functions, realtime, storage — one readable YAML file. Already have a database? init --from-db drafts it for you.

02

Generate every projection

One command emits the complete typed Dart client — models, repositories, RPC and edge function wrappers, storage clients — plus a Markdown data dictionary.

03

Prove consistency

validate reconciles database, contract, generated code, and TS types; doctor lints against best practices. CI gates on both.

supabase.yaml contract
# supabase.yaml — the single source of truth
data_model:
  public:
    things:
      ownership: workspace
      primary_key: id
      fields:
        id: uuid
        workspace_id: uuid
        name: text
        category: thing_category
        notes: text
      nullable_fields: [notes]
      enum_values:
        thing_category: [device, document, other]
      client_access:
        select: member_of_workspace
        insert: member_of_workspace

rpc_functions:
  add_thing_note:
    args: { p_thing_id: uuid, p_note: text }
    returns: uuid
your_app.dart generated client
// GENERATED by supabase_client_gen — do not edit
final repo = ThingRepository(supabase.client);

// Typed, workspace-scoped, null-safe:
final things = await repo.select(workspaceId: wsId);
final created = await repo.insert(
  {'workspace_id': wsId, 'name': 'Drill'});

// Realtime stream from the publication:
repo.stream(workspaceId: wsId)
    .listen((things) => render(things));

// RPC as a typed top-level function:
final noteId = await addThingNote(
  pThingId: thing.id, pNote: 'inspected');

// thing.category is a real Dart enum:
if (thing.category == ThingCategory.device) { ... }
ci.yaml prove it
# Gate it all in CI — drift never ships:
$ dart run supabase_client_gen:generate --contract supabase.yaml --check
$ dart run supabase_client_gen:validate --contract supabase.yaml --mode=all --with-db
$ dart run supabase_client_gen:doctor   --contract supabase.yaml --json --strict

 DB ↔ contract aligned    generated client up to date
 TS types match           0 doctor errors

Features

Everything the contract knows, every surface gets.

Built for Flutter + Supabase teams who treat generated code as a public API: versioned, reviewed, and gated in CI.

{} Typed models & repositories

Equatable models with fromJson/toJson/copyWith, repositories with typed select / insert / update / delete / stream — workspace scoping derived from the contract.

ƒ RPC functions, end to end

Each rpc_functions entry becomes a typed Dart function over client.rpc — optional args honour Postgres DEFAULTs, returns: row:<table> decodes into your models.

Composite keys & views

primary_key: [session_id, user_id] yields update/delete with one parameter per key part. kind: view generates strictly read-only repositories.

λ Edge function clients

Client-invoked edge functions become typed top-level wrappers over functions.invoke — required and optional request fields straight from the contract.

M↓ CONTRACT.md docs emitter

Every generate run writes a deterministic Markdown data dictionary of the whole backend — the fastest way to hand its shape to a colleague or an AI.

Brownfield import

init --from-db introspects an existing database into a draft contract — the prisma db pull of this tool. Offline variant: init --from-gen-types.

Doctor: best-practice linter

DR-coded findings with path and concrete fix: missing primary keys, undocumented access, public buckets, RLS disabled, SECURITY DEFINER drift — JSON output for CI.

Contract Cockpit

A zero-server, statically built visualization: interactive schema graph, security matrix, function cards, realtime & storage surface.

Drift traffic light

Doctor findings render as a red/amber/green strip in the cockpit, with status dots on affected tables in the schema graph. Drift is visible before it ships.

Deterministic, golden-tested

The same contract produces byte-identical output on any machine — no database required. Every emitter ships with golden snapshots and a compile check.

Contract Cockpit

See the whole backend at a glance.

The cockpit renders any contract as a static, zero-server site: an interactive schema graph, a security matrix of who-may-do-what, function cards, the realtime & storage surface — and a drift traffic light fed by real doctor findings.

Contract Cockpit schema graph: interactive table nodes with fields, types, and derived references
Schema graph — tables, types, derived references, click-through detail panels.
Contract Cockpit drift view: traffic light over doctor findings with severity counters
Drift view — doctor findings as a traffic light, dots on affected tables.
Open the live demo → No signup, no server — a real contract, statically baked.

Built in the open

Real product, real contract, real findings.

MIT, no strings

Everything — generator, validators, doctor, cockpit — is MIT-licensed and developed in public. Use it, fork it, ship it.

Battle-tested daily

MercyNight, an interactive party-music app, is the reference user — and the live demo shows its actual backend contract, doctor findings included.

Giving back

This tool exists because the Flutter + Supabase gap was worth closing for everyone, not just for one codebase. Issues and PRs welcome on GitHub.