CLI & project config
CLI & project config
The crouton CLI keeps your resource.json files in sync with your database. It introspects a datasource's Prisma schema, then generates new resources and reconciles existing ones β interactively, so you stay in control of every change.
pnpm add -D @ghentcdh/crouton-cli
npx crouton update resourcescrouton.json
A crouton.json file at the project root tells the CLI where things live. It holds only project-wide paths and settings β datasources are described in their own folders (see Data sources), not here.
{
"resourcesDir": "apps/backend/src/app/resources",
"dataSourcesDir": "apps/backend/src/app/data-sources",
"schemaExportName": "{Model}WithRelationsSchema",
"enumsFile": "crouton.enums.json"
}| Field | Description |
|---|---|
resourcesDir | Where resource directories live, relative to the project root. |
dataSourcesDir | Where datasource folders live. The CLI scans this to discover datasources. |
schemaExportName | Optional. Template for a model's Zod export name; {Model} β Prisma model name. Default {Model}WithRelationsSchema. |
enumsFile | Optional. Path to the shared enum registry. Default crouton.enums.json. |
If no crouton.json is found, the CLI proposes one (and a data-source.json per detected datasource) and offers to write it.
crouton create-datasource
Scaffold a new datasource β interactively, or fully via flags:
npx crouton create-datasource
# or scripted:
npx crouton create-datasource \
--name analyticsdb \
--url-env ANALYTICS_DATABASE_URL \
--generated-import @my-app/generated/analyticsdbIt reads crouton.json for dataSourcesDir, checks the name isn't already taken, and writes four files (never overwriting existing ones):
| File | Purpose |
|---|---|
<dataSourcesDir>/<name>/data-source.json | Self-describing datasource config. |
<dataSourcesDir>/<name>/index.ts | Runtime PrismaClient, connecting via the chosen env var. |
prisma/<name>/schema.prisma | Per-datasource schema with unique client and zod outputs, so multiple datasources never collide. |
prisma/<name>/prisma.config.ts | Binds this datasource's urlEnv (Prisma 7 reads the URL here, not from the schema). |
The first datasource in a project becomes the default; later ones are non-default unless you pass --default (and only one datasource may be default). Useful flags: --type (postgres/mysql/sqlite/β¦), --zod-output, --prisma-schema, --prisma-config, --client-output, --cwd, --dry-run, -y/--yes.
After scaffolding: add the urlEnv to your .env, map the generatedTypesImport to the zod output in your tsconfig paths / workspace, then run crouton update resources --datasource <name>.
crouton create-resource
Scaffold a custom resource β one whose form, table and view are configured declaratively while you implement the data access.
npx crouton create-resource zotero_itemWrites two files, and never overwrites an existing one:
resources/zotero_item/
βββ resource.json # "kind": "custom"
βββ repository.ts # typed stub, one TODO per operation| Flag | Description |
|---|---|
-k, --kind <kind> | Only custom is supported β prisma-backed resources come from crouton update resources |
-r, --route <route> | URL segment (defaults to the name) |
-t, --tag <tag> | OpenAPI tag |
--title <title> | UI title |
-d, --database <name> | Datasource exposed to the repository as ctx.prisma |
--id-type <type> | string (default) or number |
--cwd <dir> | Project directory |
--dry-run | Show the planned files without writing |
-y, --yes | Accept all defaults (non-interactive) |
crouton update resources
The command walks through:
- Pick a datasource β discovered by scanning
dataSourcesDir. Skipped when there's a single ordefaultdatasource, or pass--datasource <name>. prisma db pullβ refreshes the datasource's schema using itsprismaConfig. The current schema is backed up first; the CLI warns if it has uncommitted changes.prisma-case-formatβ automatically converts model names to PascalCase and field names to camelCase, adding@@map/@mapannotations to preserve the original database names. This means your Prisma client uses idiomatic TypeScript casing (Work,createdAt) while the database keeps its original naming (work,created_at).prisma generateβ regenerates Zod types from the formatted schema.- Pick models β choose which Prisma models to generate or update (existing ones are marked).
- Resolve changes β for each new column or change, choose per resource how to apply it: keep existing, overwrite, merge, or decide per field. New resources can be added to the sidebar.
- Preview & confirm β only changed files are listed; nothing is written until you confirm.
Useful flags:
| Flag | Effect |
|---|---|
--datasource <name> | Use a specific datasource. |
--models <a,b> | Limit to specific models (Prisma or resource names). |
--dry-run | Show the plan without writing. |
--yes | Non-interactive; accept recommended choices. |
--skip-pull / --skip-generate | Skip the Prisma steps. |
--no-draft | Write new resources without draft: true (served immediately). |
What gets generated
For each model, the CLI writes a resource.json (columns as a map, sensible defaults applied) and, when absent, a sibling schema.ts that re-exports the generated Zod schema from the datasource's generatedTypesImport. A hand-written schema.ts is never overwritten.
Every generated resource.json is stamped with a $schema URL and the current schemaVersion (see Versioning), so it validates and autocompletes in your editor and starts at the current version.
Defaults applied during generation:
- new resources are written with
draft: trueso a generated-but-unreviewed resource isn't served by accident β flip it tofalse(or remove it) when ready, or pass--no-draftto skip it. See Draft resources; - relations are ignored (not added as columns) unless explicitly enabled;
id, and create/update timestamps are hidden in the table;description-style string fields default to atextareainput;- enum columns become
{ value, label }selects backed by the shared registry.
Tips
crouton update resources ignores custom resources. They have no Prisma model, so introspection would see every column as missing from the database and offer to remove it.
Enum registry
Enum option lists live once in a project-level crouton.enums.json so every resource references the same labels instead of duplicating them:
{
"author_origin_enum": [
{
"value": "wikidata",
"label": "Wikidata"
},
{
"value": "syriaca",
"label": "Syriaca",
"disabled": true
}
],
"text_type_enum": [
{
"value": "original",
"label": "Original"
},
{
"value": "translation",
"label": "Translation"
}
]
}A column references a list by name:
{
"text_type": {
"enum": "text_type_enum",
"displayKey": "label",
"fieldInput": {
"options": {
"emitObject": true
}
}
}
}At load time the referenced list is injected into the column's options, so the table shows the label while forms submit the scalar value. crouton update resources merges newly discovered enum values into the registry, preserving labels and order you've edited by hand and never dropping existing entries.
The registry is loaded by walking up from the resources directory, or via the enumsFile option to forResourceDir (see Backend setup). In production, deploy crouton.enums.json somewhere that walk-up can reach, or pass enumsFile explicitly.
Keeping the CLI fresh
When you consume crouton via file: links, package managers install a copy of the built CLI, not a live link to dist. After rebuilding crouton, re-run your install (e.g. pnpm install / pnpm install --force) so the binary you run is the rebuilt one.