Chapter 04

Generate the application

Two ways in: the CLI, which is what you will use once you trust the model, and the browser designer, which is better the first time because it renders the ERD, validates as you type and keeps versions. Both call the same pipeline, so both produce the same application.

Path A — the command line

  1. Check the model

    Optional, because the generator runs the checker itself, but it is instant and it tells you the truth before anything is written.

    shell
    bun language/checker.ts language/examples/crm.eml.mmd
    # crm.eml.mmd  0 errors · 0 warnings
  2. Generate

    One command. --no-setup keeps it from installing and migrating straight away, which you want the first time so you can look at what it produced.

    shell
    bun packages/generator/src/cli/generate.ts generate \
      --input language/examples/crm.eml.mmd \
      --output generated-projects/crm \
      --name crm \
      --port 4001 --frontend-port 4000 \
      --records-per-entity 25 \
      --force --no-setup

    It prints what it found as it goes: entities and their attribute counts, then the rules, hooks, sagas, state machines and categories it compiled. Read that list — it is the fastest way to notice a directive that did not parse.

  3. Point it at a database

    shell
    cd generated-projects/crm
    cp backend/.env.example backend/.env
    # edit backend/.env:
    # DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/crm

    The generated .env.example guesses a local socket connection for your machine. It is a guess; the URL is the one line worth checking by hand.

  4. Install, migrate, seed

    shell
    bun install
    bun run db:setup   # migrations, then every seed in order

    The seeds run in a deliberate order: users and roles, then reference types and your model's enums, then the dictionary (tables, columns, windows, tabs, fields), then categories, then sample business data, then rule definitions, then workflow definitions. The last two are the model's rules and workflows arriving in the database.

  5. Run it

    shell
    bun run dev   # backend :4001, frontend :4000

    Sign in with the admin user the backend creates on first start — ADMIN_EMAIL and ADMIN_PASSWORD in backend/.env, admin@admin.com / admin by default.

Useful flags

--dry-run lists the files it would write without writing them. --records-per-entity sizes the seeded sample data (1000 by default — use 25 while you are iterating). --skip-frontend / --skip-backend generate one half. --run-tests-fast runs the generated suite afterwards, skipping the bulk-seed volume tests.

Path B — the browser

The generator ships as an application in its own right. Run bun run dev at the repository root and open localhost:3000. It walks a project through six steps: define, discover, logic, generate, enhance, deploy.

Sign in. The first account you register needs promoting to admin: bun run seed:admin -- --email you@example.com.
The AppWithAI sign-in screen.
Where you land. Projects, a Mermaid library, a rules administration area, and two ways in: describe a domain from scratch, or import a model you already have.
The generator's project dashboard with an empty state reading 'No projects yet', a project search box, status and type filters, and buttons for Mermaid Library, Rules Admin, Create New Project and Import a model.
Import a model. Start from a .mmd file you already have rather than describing a domain from scratch.
The Import a model dialog, asking for a .mmd file and a project name.
The design step. Source on the left, live ERD on the right, entity count in the header. The toolbar carries Entities, Versions, Flow View, DB Ops, Validate, Save Draft, Import and Export.
The design step: Mermaid ERD source on the left, live rendered ERD on the right, with entity count and toolbar.
The whole journey on one screen. Six steps across the top, the model's 1,117 lines on the left, every one of its seventeen entities drawn on the right, and an AI assistant at the foot that takes a plain-English instruction and edits the model — with auto-retry, so a failed validation is handed back to the model to fix up to three times rather than to you.
Step 2 of 6, Discover Your Data Model: a six-stage progress stepper reading Init, Design, Logic, Gen, Enhance, Deploy; a Your Journey strip reading Define, Discover, Generate, Add Features, Deploy; the 1,117-line Mermaid ERD source on the left; a live preview of all 17 entity boxes and their relationships on the right; and an AI Assistant bar at the foot with auto-retry enabled.

Validate renders the diagram through the same normalizer the preview uses and reports the result: the entity count when it parses, and Mermaid's own error with a line number and caret when it does not.

A deliberate mistake. The banner gives you the line, the expectation and the character that broke it — the same message Mermaid would give a renderer.
The design step showing a red validation banner reading 'The diagram could not be parsed' with a parse error on line 2 and a caret pointing at the offending character.
Which path should you use?

Use the browser while the shape of the model is still moving: the live ERD catches a wrong cardinality faster than reading operators, and Versions gives you a way back. Use the CLI once the model lives in version control, because it is scriptable, it is what CI runs, and it produces the same output.

What lands on disk

treegenerated-projects/crm — 454 files
crm/
├── backend/                 # NestJS + Fastify + Kysely
│   ├── migrations/          # bus_ tables, sys_ dictionary, indexes
│   ├── seeds/               # users, references + your enums, dictionary,
│   │                        #   categories, sample data, rules, workflows
│   └── src/modules/
│       ├── bus/             # generic CRUD over every business entity
│       ├── sys/             # the Application Dictionary API
│       ├── rules/           # GoRules evaluation
│       ├── workflow/        # the step executor
│       ├── hooks/handlers/  # ⭐ your hook bodies live here, never overwritten
│       ├── audit/           # append-only change log
│       └── auth/            # sessions, roles, permissions
├── frontend/                # TanStack Start + React 19 (198 files)
│   └── src/
│       ├── routes/          # one screen per entity, plus /admin/*
│       ├── components/      # dynamic table, dynamic form, admin shells
│       └── hooks/           # entity, field and lookup queries
├── tests/                   # generated bun:test end-to-end suite (77 files)
├── model/                   # the .mmd this was generated from, shipped along
├── docker/ · Dockerfile · docker-compose.yml
└── .github/workflows/       # CI for both halves
17
bus_ tables
25
rule definitions
39
workflow definitions
23
dictionary windows
26
enum references
143
enum values

Regenerating safely

You will regenerate constantly — the model changes, the application follows. The rules about what survives are simple and worth knowing before you start editing generated code.

WhatOn regeneration
hooks/handlers/<Entity>.tsKept. Written once; new hooks are appended. Your logic is safe.
hooks/handlers/index.tsRewritten — it is pure wiring.
Workflows and rules marked from the modelRewritten. The model owns them.
Workflows built in the app's own designerKept. They carry source: designer and regeneration never touches them.
Dictionary help you editedKept. Seeds backfill help only where it is null.
Everything else under src/Rewritten. Treat it as build output.
Restart after regenerating

Regenerating replaces the front-end source underneath a running dev server, which usually kills it. Stop the app, regenerate, run bun run db:setup if the model's schema, enums, rules or workflows changed, then start it again.