Generated File Map
Exactly what grit generate resource writes, for every app in your project — the definitive index of paths, what each file is, and which are yours to edit versus regenerated. Filenames use the resource name (singular for Go, kebab-plural for routes/hooks).
One command, the whole stack
From a single field spec, Grit fans out a working, end-to-end feature. Here are the four files at the heart of it — switch tabs to see the same resource across Go and TypeScript:
type Product struct {ID string `gorm:"primaryKey;size:36" json:"id"`Name string `gorm:"size:255;not null" json:"name" binding:"required"`Price float64 `gorm:"not null" json:"price"`CategoryID string `gorm:"size:36;not null;index" json:"category_id"`Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`CreatedAt time.Time `json:"created_at"`UpdatedAt time.Time `json:"updated_at"`DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`}func (p *Product) BeforeCreate(tx *gorm.DB) error {if p.ID == "" {p.ID = uuid.New().String()}return nil}
Excerpts, abbreviated for clarity — the real files are complete and runnable.
Backend + shared (always)
These are written for every architecture that has an API and a shared package.
| Path | What it is |
|---|---|
| apps/api/internal/models/<name>.go | GORM model (UUID PK, BeforeCreate, relations) |
| apps/api/internal/services/<name>.go | Service layer — your custom business logic goes here |
| apps/api/internal/handlers/<name>.go | Gin CRUD handler (list/get/create/update/delete) |
| apps/api/internal/handlers/<name>_import.go | CSV/Excel bulk-import handler |
| packages/shared/schemas/<name>.ts | Zod Create/Update schemas |
| packages/shared/types/<name>.ts | TypeScript types (regenerated by grit sync) |
Web app (if present)
| Path | What it is |
|---|---|
| apps/web/hooks/use-<plural>.ts | React Query hooks (Next.js) — src/hooks/ for the Vite kit |
Admin panel (if present)
| Path | What it is |
|---|---|
| apps/admin/resources/<name>.ts | Resource definition (table + form + widgets) — src/resources/ on Vite |
| apps/admin/app/(dashboard)/resources/<plural>/page.tsx | Admin CRUD page — src/routes/_dashboard/resources/ on Vite |
Mobile — Expo (if present)
| Path | What it is |
|---|---|
| apps/expo/hooks/use-<plural>.ts | Typed React Query hook |
| apps/expo/app/<plural>/index.tsx | List screen |
| apps/expo/app/<plural>/[id].tsx | Detail screen |
| apps/expo/app/<plural>/new.tsx | Create screen |
| apps/expo/app/<plural>/edit/[id].tsx | Edit screen |
| apps/expo/components/resource-forms/<plural>-form.tsx | Shared form component |
Desktop — Wails (if present)
| Path | What it is |
|---|---|
| apps/desktop/frontend/src/hooks/use-<plural>.ts | React Query hook (calls Wails bindings) |
| apps/desktop/frontend/src/routes/_app/<plural>.index.tsx | List route |
| apps/desktop/frontend/src/routes/_app/<plural>.new.tsx | Create route (+ detail / edit routes) |
Files it edits (marker injection)
Rather than overwrite shared files, Grit injects at // grit:* markers. Keep the markers in place — they're how generation and seeding stay idempotent.
Routes are the exception, and used to be the worst case: four edits per resource, hundreds of lines apart, in a routes.go that passed a thousand lines with a handful of resources. Each resource now gets its own internal/routes/<resource>_routes.go holding the handler and every path that reaches it, and routes.go no longer changes when you add or remove one.
| Path | What it is |
|---|---|
| apps/api/internal/models/user.go | Model registered in Models() at // grit:models |
| packages/shared/schemas/index.ts | Schema re-exported at // grit:schemas |
| apps/admin/resources/index.ts | Resource registered at // grit:resources |
internal/routes/<resource>_routes.go and add a line. The file registers itself from an init(), so creating one mounts a resource and deleting one unmounts it; nothing else refers to it by name. m.Public is outside the auth middleware and behind an API key, m.Protected takes a JWT or an API key, and m.Admin also requires the ADMIN role. Mount is defined in internal/routes/resources.go.packages/shared/types/*, which grit sync rewrites from your Go structs every time it runs.grit generate resource writes these files again from scratch. If you have edited them, Grit compares each one against what it last wrote, stops before touching anything, and names the files at risk. Nothing is lost, but nothing is generated either.To add a field without that, add it in place with
grit generate field <Name> <name:type>, then grit migrate. It reaches the model, the handler's request structs and PATCH allow-list, the CSV importer, the types, the Zod schemas and the admin, and leaves the rest of each file as you had it. It covers scalar, select and toggle fields.Adding the field to the model by hand and running
grit sync is not enough on its own. Sync updates the types, the schemas and the admin, but the API accepts only what the handler's request structs name, so the field is shown, submitted, and dropped.--force regenerates anyway, overwriting your edits. Commit first: there is no backup.grit generate remove resource <Name> deletes exactly these files and unwinds the marker injections — a clean inverse of generation.