# ADR 0006: NestJS + Prisma for the API server - Status: accepted - Date: 2026-07-04 ## Context The API server carries most business logic: auth, permission resolution, pond/page/label CRUD, quotas, import/export orchestration, plugin management, e-mail, admin functions. Stories are implemented by many independent contributors in 0.5–2 day slices; the framework must make module boundaries, dependency injection, validation, and testing conventions explicit so parallel work does not collide. ## Decision - **NestJS** (Express adapter) structures `apps/api` into feature modules that mirror the domain: `auth`, `users`, `ponds`, `pages`, `labels`, `permissions`, `search`, `uploads`, `import-export`, `plugins`, `quotas`, `comments`, `notifications`, `admin`, `setup`, `mail`, `health`. - **Prisma** is the ORM: schema-first data model (`schema.prisma` is the single source of truth, mirrored in `data-model.md`), generated type-safe client, `prisma migrate` for migrations (applied automatically at startup, ADR 0002). Raw SQL is allowed where Prisma falls short (FTS queries, ADR 0010; Yjs state upserts). - API style: **REST + JSON** under `/api/v1`, request/response validated with Zod schemas from `packages/shared` (single definition for client and server). OpenAPI document generated from these schemas for documentation. - Cross-cutting rules: - Every route passes an authentication guard and a **permission guard** that calls the shared resolution algorithm (`permissions.md`) — no ad-hoc permission checks inside handlers. - All external side effects (mail, conversion sidecars) go through dedicated injectable services so tests can fake them. ## Consequences - Stories can say "add endpoint X in module Y, guard with permission Z" and be implemented without architectural decisions; NestJS's DI and testing utilities give a uniform unit/e2e test pattern (Vitest + supertest). - Prisma migrations serialize schema changes; stories touching the schema must be sequenced (flagged in issue dependencies). - NestJS adds some boilerplate per module; we accept this for the consistency it buys in a many-contributors setting. ## Alternatives considered - **Fastify/Express hand-rolled**: less boilerplate, but every contributor invents structure; consistency would depend on discipline instead of framework rails. Rejected for this team model. - **tRPC**: excellent DX for a closed SPA+API pair, but a REST API is a deliberate product feature for self-hosters and integrations. - **Drizzle**: fine ORM, but Prisma's schema file + migration story is the most widely known and the easiest to review.