Development Guide
This guide is for contributors working on the Grant platform itself. If you are integrating Grant into your application, see the Integration Guide.
Project Structure
├── apps/
│ ├── api/ # Apollo Server + Express REST API
│ │ ├── src/
│ │ │ ├── config/ # Environment configuration
│ │ │ ├── graphql/ # Resolvers (queries, mutations, field resolvers)
│ │ │ ├── handlers/ # Orchestration layer (caching, transactions)
│ │ │ ├── services/ # Business logic and validation
│ │ │ ├── repositories/ # Database access (Drizzle ORM)
│ │ │ ├── rest/ # REST routes, schemas, OpenAPI
│ │ │ ├── middleware/ # Express middleware
│ │ │ └── lib/ # Shared utilities (logger, errors, auth)
│ │ └── tests/ # Unit, integration, and E2E tests
│ └── web/ # Next.js 15 dashboard (App Router)
├── packages/@grantjs/
│ ├── schema/ # GraphQL schema + codegen types
│ ├── core/ # Domain ports, interfaces, exceptions
│ ├── constants/ # Resource definitions, permissions, groups
│ ├── database/ # Drizzle schemas, migrations, seeds
│ ├── logger/ # Pino adapter
│ ├── errors/ # HTTP error adapter
│ ├── cache/ # Redis / in-memory adapter
│ ├── storage/ # S3 / local adapter
│ ├── email/ # SMTP / SES adapter
│ ├── jobs/ # node-cron / BullMQ adapter
│ ├── server/ # Server SDK (Express, Fastify, NestJS, Next.js)
│ ├── client/ # Browser SDK (React hooks, GrantGate)
│ └── cli/ # Grant CLI
└── docs/ # VitePress documentation siteLayer Boundaries
Transport (GraphQL resolvers, REST routes)
→ Handlers (orchestration, caching, transactions)
→ Services (business logic, validation, audit logging)
→ Repositories (Drizzle ORM, field selection)
→ Database (PostgreSQL + RLS)Hard rules:
- Handlers never import repositories — they go through services
- Repositories never import services or handlers
- REST/GraphQL resolvers never import repositories — they call handlers only
- Import logger from
@/lib/logger, errors from@/lib/errors, shared types from@/types
Development Workflow
bash
# 1. Start infrastructure
docker compose up -d
# 2. Install dependencies
pnpm install
# 3. Run database migrations and seed
pnpm --filter @grantjs/database db:migrate
pnpm --filter @grantjs/database db:seed
# 4. Generate schema types (after GraphQL changes)
pnpm --filter @grantjs/schema generate
# 5. Start dev servers
pnpm --filter api dev # API on :4000
pnpm --filter web dev # Web on :3000Adding a GraphQL Feature
- Schema — Define types, inputs, queries, and mutations in
packages/@grantjs/schema/src/schema/{feature}/ - Operations — Add operation documents in
packages/@grantjs/schema/src/operations/{feature}/ - Generate — Run
pnpm --filter @grantjs/schema generateto produce TypeScript types - Repository — Add database access methods in
apps/api/src/repositories/ - Service — Add business logic with Zod validation in
apps/api/src/services/ - Handler — Add orchestration (caching, transactions) in
apps/api/src/handlers/ - Resolver — Register query/mutation resolvers in
apps/api/src/graphql/resolvers/
Adding a REST Endpoint
See Adding REST Endpoints for the full step-by-step guide covering Zod schemas, routes, RBAC guards, and OpenAPI registration.
Code Style
- TypeScript strict mode everywhere
- Conventional Commits:
feat:,fix:,docs:,chore: - Use
@/and@grantjs/*aliases — no deep relative paths - Use
import typefor type-only imports - Use domain-specific errors (
NotFoundError,ValidationError) — never rawthrow new Error(...) - Use the structured logger — never
console.login runtime code
Contributing
- Fork the repository
- Create a feature branch from
main - Follow the layer boundaries and code style
- Ensure
pnpm buildandpnpm testpass - Open a pull request
Related:
- Architecture Overview — System design and package graph
- Adding REST Endpoints — REST development guide
- Testing — Test setup and patterns