Generators
One command scaffolds a whole vertical slice, from the Drizzle table to the react-query hook.
Adding a resource by hand means touching a table, a model, a service, a controller, the route registration, shared schemas, hooks and the OpenAPI spec — eight places, in a fixed order, every time. The generator does that pass deterministically.
bun run generate resource projects
bun run generate job send-digest
bun run generate webhook-event project.completedapps/web/app/[locale]/dashboard/projects in the running demo is what a
generated resource looks like once it has a screen.
What resource writes
It also registers the controller in the v1 router and refreshes
apps/api/openapi.json, which means the new routes appear in the Scalar
reference and as MCP tools without another step.
The generated service.test.ts is a guard test, not a placeholder: it asserts
the ownership scoping that keeps one user's rows away from another's.
Flags
| Flag | Effect |
|---|---|
--entitlement=<feature> | Gates the routes on a feature from the entitlements catalogue |
--audit | Writes changes through the audit-log helper |
--singular=<word> | When the plural is irregular and the derived singular would be wrong |
--force | Overwrite existing files instead of skipping them |
--json | Emit the manifest as JSON |
It is safe to re-run
Existing files are skipped, not clobbered, and in-place registrations are applied once. Running the same command twice is a no-op rather than a mess, which is what makes it usable from a script.
Every run prints a manifest of what it created, updated and skipped. With
--json that manifest is machine-readable, so an agent can generate a slice
and then act on exactly the files it touched instead of guessing.
The generator does not run db:push. It appends the table to the schema and
stops, so you can read the diff before it reaches a database. Push or generate
a migration yourself — see database for which of those
you want.
After generating
The scaffold is correct and generic. What it cannot know is your domain: the
columns beyond id, ownerId and timestamps, the validation that matters, and
whether a list endpoint should paginate. Open model.ts first — the schemas
there drive both the API validation and the OpenAPI spec, so tightening them is
the highest-leverage edit.