Folder conventions
internal/, packages/shared, apps/: the rules and the rationale.
Grit has one place for everything. That sounds like a constraint ā it's actually a superpower. Every team member, every AI agent, every future-you knows where to look without thinking. This lesson covers the rules.
The four big places
- apps/api/cmd/ ā entry points (just
server/main.gousually) - apps/api/internal/ ā everything that's not exported. The actual code lives here.
- apps/web/ + apps/admin/ ā Next.js apps. Standard App Router shape.
- packages/shared/ ā Zod + TS types. Imported by web and admin.
internal/ ā the seven sub-folders that matter
internal/āāā config/ Loads .env into a typed Config struct.āāā database/ Postgres connection + AutoMigrate.āāā handlers/ HTTP handlers ā thin, one per resource.āāā middleware/ auth, CORS, security, request ID, etc.āāā models/ GORM struct definitions.āāā routes/ routes.go ā mounts every handler on a route.āāā services/ Business logic ā called by handlers.
The flow of a request
HTTP requestā middleware (auth, CORS, security)ā routes/routes.go picks the handlerā handlers/x.go parses input, calls servicesā services/x.go does the work, calls modelsā models/x.go is the GORM struct used to read/write DBā response shaped by handler, sent back
packages/shared/ ā the type bridge
packages/shared/āāā src/ā āāā schemas/ Zod schemas (used to validate API calls)ā āāā types/ TS types (generated by grit sync from Go structs)ā āāā constants/ Route constants, enums, shared values.āāā package.json Imported as @workspace/shared by web + admin
What does NOT go in internal/
- Tests ā they live alongside the file they test:
handlers/user.goāhandlers/user_test.go. - Migrations ā Grit uses GORM AutoMigrate by default. No
internal/migrations/folder. - Static assets ā frontend assets live in
apps/web/public/orapps/admin/public/.
Quick check
Try it
You need to add a NotificationService that sends push notifications when a user's order ships. Decide where each piece goes ā write it in notes.md:
- The struct + methods that talk to Expo Push?
- The DB row that tracks "was this notification sent?"
- The HTTP endpoint to trigger a test push from the admin panel?
- The TypeScript type for the admin form's payload?
What's next
Folders sorted. Next ā naming. snake_case for Go files, kebab-case for TS, plural for routes. Same logic: one rule, applied everywhere.
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