Initial deliverable of the architecture phase: 16 ADRs (stack, CRDT collaboration, plugin sandbox, import/export, backups, CI/CD), data model, permission model, real-time collaboration and plugin concepts, deployment/operations/security documentation, and the milestone roadmap that the implementation issues are derived from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2.6 KiB
2.6 KiB
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/apiinto 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.prismais the single source of truth, mirrored indata-model.md), generated type-safe client,prisma migratefor 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 frompackages/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.
- Every route passes an authentication guard and a permission guard
that calls the shared resolution algorithm (
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.