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>
55 lines
2.6 KiB
Markdown
55 lines
2.6 KiB
Markdown
# 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.
|