What is a resource?
Model + handler + service + types + routes — the standard slice.
Before we run the generator, you need a word for what it generates. Spoiler: it's a resource. The rest of this chapter goes deep on the generator — this 5-minute lesson nails down the concept so the next four lessons land properly.
A resource is a complete vertical slice
For each domain concept in your app (User, Contact, Order, Product, Invoice…), Grit produces a slice that spans the whole stack. Not just "a model" — every layer that touches that concept gets a file:
Model GORM struct apps/api/internal/models/contact.go (new)Service Business logic apps/api/internal/services/contact.go (new)Handler HTTP layer apps/api/internal/handlers/contact.go (new)Zod schema Validation packages/shared/schemas/contact.ts (new)TypeScript type Frontend type packages/shared/types/contact.ts (new)React Query hook Data fetching apps/web/hooks/use-contacts.ts (new)Admin definition Filament-style def apps/admin/resources/contacts.ts (new)Admin page Thin page wrapper apps/admin/(dashboard)/resources/contacts/page.tsx (new)Plus ~10 marker-fenced injections into existing files:- API routes.go (route group + handler init + AutoMigrate + Studio list)- packages/shared/{schemas,types,constants}/index.ts (re-exports)- apps/admin/resources/index.ts (registry import + array entry)
That's eight brand-new files plus a handful of injections into existing files — too much to write by hand every time you add an entity, and too easy to drift out of sync if you do (a field on the Go struct that's missing from the TS type; a route the admin page forgot to call). The generator writes all eight files and applies every injection in one command, all keyed off marker comments so re-runs are idempotent and removal is clean.
The split between the admin definition and the admin page is the one that surprises most people the first time. The definition (resources/contacts.ts) is where you describe columns, form fields, badge colors, and export rules; the page (page.tsx) is a six-line wrapper that hands the definition to a generic <ResourcePage>. The wrapper exists so you can drop down to a custom layout when you need to (you almost never do).
The mental model: CRUD plus a UI
A resource exists to do CRUD on a thing:
- Create —
POST /api/contacts - Read one —
GET /api/contacts/:id - Read many —
GET /api/contacts(paginated, searchable) - Update —
PUT /api/contacts/:id - Delete —
DELETE /api/contacts/:id
Five endpoints, one model, one admin page. The handler, service, type, schema, hook, and admin page all line up so that flow works end-to-end. If a feature in your head expands to all five verbs and someone (a user, an admin, support) clicks a button to invoke them — it's probably a resource.
Fields define the shape
When you generate a resource, you tell the generator what fields the model has. One simple example you'll see again next lesson:
$grit generate resource Contact \$ --fields "name:string,email:string:unique,phone:string:optional"
Each field becomes seven things at once: a GORM column, a Go struct field, a Zod validator, a TypeScript property, an admin form input, a DataTable column, and a search match (if the type is searchable). The generator owns that mapping table — you just describe the shape.
Quick check
Try it
In your notes.md, list 5 concepts from a real product you know (your job's app, a side project, an app you use daily). For each, write "resource" or "not a resource" and one sentence why.
What's next
You know what a resource is. Next lesson we dissect the command itself — the anatomy of grit generate resource Contact --fields "…" — and then we generate Contact end-to-end and watch the eight files appear.
Spot a typo? Have an idea?
Help us improve this lesson. One click opens a GitHub issue with the lesson URL pre-filled — suggest clearer wording, report a bug, or request more depth. The course keeps improving thanks to learners like you.
Suggest an improvement on GitHub