Project Structure
A complete guide to the Grit monorepo layout. Every Grit project follows this exact structure, so any developer (or AI assistant) can jump in and know exactly where everything lives.
Overview
Grit uses a Turborepo-powered monorepo with three applications and one shared package. The Go API, Next.js web app, Next.js admin panel, and shared TypeScript package all live in a single repository with shared configuration.
myapp/
├── .env # Environment variables
├── .env.example # Template with documentation
├── .env.cloud.example # Cloud-only setup (no Docker)
├── .gitignore # Git ignore rules
├── docker-compose.yml # Dev: PostgreSQL, Redis, MinIO, Mailhog
├── docker-compose.prod.yml # Production: multi-stage builds
├── grit.config.ts # Grit framework configuration
├── package.json # Root package.json (workspace scripts)
├── pnpm-workspace.yaml # pnpm workspace definition
├── turbo.json # Turborepo task configuration
├── README.md # Project documentation
│
├── apps/
│ ├── api/ # Go backend (Gin + GORM)
│ ├── web/ # Next.js main frontend
│ └── admin/ # Next.js admin panel
│
└── packages/
└── shared/ # Shared Zod schemas, TS types, constantsRoot Files
The root of the monorepo contains configuration files that apply to the entire project:
.envAll environment variables for the project -- database URL, JWT secret, Redis, storage, email, AI config. This file is gitignored.
.env.exampleDocumented template of all environment variables with sensible defaults. Committed to git so new developers know what variables are needed.
docker-compose.ymlDevelopment services: PostgreSQL 16, Redis 7, MinIO (S3-compatible storage), and Mailhog (email testing). Run with docker compose up -d.
docker-compose.prod.ymlProduction setup with multi-stage Docker builds for the Go API and Next.js apps.
grit.config.tsGrit framework configuration -- project name, API URL, and other framework-level settings.
turbo.jsonTurborepo configuration defining build, dev, and lint tasks with dependency relationships and caching.
pnpm-workspace.yamlDefines the pnpm workspace: apps/* and packages/* directories are included.
package.jsonRoot package.json with workspace-level scripts like dev, build, and lint.
Go API (apps/api/)
The Go backend is a Gin web server with GORM ORM. It follows Go conventions with an internal/ directory for private packages and cmd/ for the entry point.
apps/api/
├── go.mod # Go module definition
├── go.sum # Dependency checksums
├── Dockerfile # Multi-stage production build
├── .air.toml # Hot reload configuration
│
├── cmd/
│ └── server/
│ └── main.go # Entry point: init config, DB, router, start server
│
└── internal/
├── config/
│ └── config.go # Load .env, parse config struct
├── database/
│ └── database.go # GORM connection, auto-migration
├── models/
│ ├── user.go # User model (built-in)
│ └── post.go # Generated models go here
├── handlers/
│ ├── auth.go # Auth endpoints (login, register, etc.)
│ ├── user.go # User CRUD endpoints
│ └── post.go # Generated handlers go here
├── services/
│ ├── auth.go # JWT generation, token validation
│ ├── user.go # User business logic
│ └── post.go # Generated services go here
├── middleware/
│ ├── auth.go # JWT validation, role-based access
│ ├── cors.go # CORS configuration
│ └── logger.go # Structured JSON logging
├── routes/
│ └── routes.go # Route registration (all endpoints)
├── cache/
│ └── cache.go # Redis caching service
├── storage/
│ └── storage.go # S3/R2/MinIO file storage
├── mail/
│ ├── mailer.go # Resend email service
│ └── templates/ # HTML email templates
├── jobs/
│ └── jobs.go # Asynq background job queue
├── cron/
│ └── cron.go # Asynq cron scheduler
└── ai/
└── ai.go # AI integration (Claude + OpenAI)Key Conventions
- Models define GORM structs with json, gorm, and binding tags. One file per model.
- Handlers are thin HTTP controllers. They parse requests, call services, and return responses. No business logic in handlers.
- Services contain business logic. They interact with the database through GORM and are called by handlers.
- Middleware runs before handlers. Auth, CORS, and logging are pre-configured.
- Routes are registered in a single file. Each resource group is clearly separated.
Web App (apps/web/)
The main Next.js frontend application. Uses the App Router with route groups for auth and dashboard sections.
apps/web/
├── package.json
├── next.config.ts
├── tailwind.config.ts
├── Dockerfile
│
├── app/
│ ├── layout.tsx # Root layout with providers
│ ├── page.tsx # Landing page
│ ├── (auth)/ # Auth route group (no layout)
│ │ ├── login/page.tsx
│ │ ├── register/page.tsx
│ │ └── forgot-password/page.tsx
│ └── (dashboard)/ # Protected routes
│ ├── layout.tsx # Dashboard layout (sidebar + navbar)
│ └── dashboard/page.tsx # Main dashboard
│
├── components/
│ ├── ui/ # shadcn/ui components
│ └── shared/ # App-specific components
│
├── hooks/
│ ├── use-auth.ts # Auth hooks (login, register, logout, me)
│ └── use-posts.ts # Generated resource hooks
│
└── lib/
├── api-client.ts # Axios instance with JWT interceptor
├── auth.ts # Auth utilities (token storage)
└── utils.ts # Utility functionsKey Conventions
- Route groups
(auth)and(dashboard)organize pages without affecting the URL structure. - Hooks wrap React Query mutations and queries. All data fetching goes through hooks, never raw fetch calls in components.
- API client is a pre-configured Axios instance that automatically injects JWT tokens and handles token refresh.
- UI components come from shadcn/ui. Add more with
pnpm dlx shadcn@latest add button.
Admin Panel (apps/admin/)
The Filament-like admin panel. A separate Next.js app that provides resource management with data tables, forms, and dashboard widgets.
apps/admin/
├── package.json
├── next.config.ts
├── tailwind.config.ts
│
├── app/
│ ├── layout.tsx # Admin layout with sidebar
│ ├── page.tsx # Dashboard with widgets
│ └── resources/
│ ├── users/page.tsx # User management page
│ └── posts/page.tsx # Generated resource pages
│
├── components/
│ ├── layout/
│ │ ├── admin-layout.tsx # Admin shell
│ │ ├── sidebar.tsx # Collapsible sidebar
│ │ └── navbar.tsx # Top navigation bar
│ ├── tables/
│ │ ├── data-table.tsx # Server-side paginated table
│ │ ├── columns.tsx # Column definitions
│ │ └── filters.tsx # Table filters
│ ├── forms/
│ │ ├── form-builder.tsx # Dynamic form renderer
│ │ ├── fields/ # Field type components
│ │ └── form-modal.tsx # Modal form wrapper
│ └── widgets/
│ ├── stats-card.tsx # Stat number + trend
│ ├── chart-widget.tsx # Recharts wrapper
│ └── recent-activity.tsx # Activity feed
│
├── hooks/
│ ├── use-auth.ts # Admin auth hooks
│ └── use-posts.ts # Generated resource hooks
│
└── resources/ # Resource definitions (THE MAGIC)
├── index.ts # Resource registry
├── users.ts # User resource config
└── posts.ts # Generated resource configsKey Conventions
- Resource definitions in
resources/define the table columns, form fields, filters, and actions for each resource. This is how the admin panel generates its UI. - Data tables are server-side paginated. They communicate directly with the Go API for sorting, filtering, and searching.
- Form builder renders forms dynamically from resource definitions. Field types include text, number, select, date, toggle, and file upload.
- Widgets are dashboard components that fetch data from the API and display stats, charts, and activity feeds.
Shared Package (packages/shared/)
The shared package contains TypeScript types, Zod validation schemas, and constants used by both the web app and admin panel. This is the glue that keeps the frontend in sync with the Go backend.
packages/shared/
├── package.json
│
├── schemas/ # Zod validation schemas
│ ├── user.ts # User create/update schemas
│ ├── post.ts # Generated schemas
│ └── index.ts # Re-exports all schemas
│
├── types/ # TypeScript types
│ ├── user.ts # User type + API response types
│ ├── post.ts # Generated types
│ ├── api.ts # Pagination, error, response types
│ └── index.ts # Re-exports all types
│
└── constants/
└── index.ts # Roles, API routes, config constantsKey Conventions
- Schemas are Zod validation schemas that match the Go model struct tags. They are the source of truth for frontend validation.
- Types are TypeScript interfaces that mirror Go structs. Generated by
grit syncfrom the Go model definitions. - Constants include role strings, API route paths, and configuration values shared between all frontend apps.
- Both
apps/webandapps/adminimport from@shared/schemas,@shared/types, and@shared/constants.
Where Things Go
A quick reference for where to put different types of code:
| What | Where |
|---|---|
| New database model | apps/api/internal/models/<name>.go |
| API endpoint handler | apps/api/internal/handlers/<name>.go |
| Business logic | apps/api/internal/services/<name>.go |
| Auth middleware | apps/api/internal/middleware/auth.go |
| API route registration | apps/api/internal/routes/routes.go |
| Environment config | apps/api/internal/config/config.go |
| Background job | apps/api/internal/jobs/jobs.go |
| Email template | apps/api/internal/mail/templates/ |
| Zod validation schema | packages/shared/schemas/<name>.ts |
| TypeScript type | packages/shared/types/<name>.ts |
| React Query hook | apps/web/hooks/use-<names>.ts |
| Admin resource page | apps/admin/app/resources/<names>/page.tsx |
| Admin resource definition | apps/admin/resources/<names>.ts |
| Reusable UI component | apps/web/components/shared/ |
| shadcn/ui component | apps/web/components/ui/ |
| Dashboard widget | apps/admin/components/widgets/ |
Generated vs. Hand-Written Code
When you run grit generate resource, the CLI creates files in all the locations listed above. These generated files are yours to modify. The CLI uses marker comments like // grit:inject-routes and // grit:inject-models to know where to inject new code into existing files (like routes.go and database.go).
Do not remove these marker comments. They are how the CLI knows where to add new routes and model registrations when you generate additional resources.