Files
web_sport/docs/adr/0006-zod-schemas-shared-between-api-and-frontends.md

36 lines
1.5 KiB
Markdown

# ADR-0006: Zod schemas shared between API and frontends
- **Status:** Accepted
- **Date:** 2026-08-11
## Context
Validation rules exist twice by default: once in the API and once in the form. They
drift, and the drift shows up as a form that accepts input the server rejects.
## Decision
One Zod schema per input shape, defined in `@sport/validation` and imported by both
sides. The API applies it through `ZodValidationPipe`; the frontends apply the same object to
their forms. Zod was chosen over class-validator specifically because a class with decorators
cannot cross into a React form, whereas a schema object can.
Scope is deliberately limited to shape and format rules. Anything requiring database state —
"is this coupon still valid", "is this variant in stock" — is a business rule and lives in the
backend service layer.
## Consequences
A rule change happens once. Field-level errors come back keyed by dotted path
(`items.0.quantity`), which forms consume directly. The parsed output carries coercions and
defaults, so controllers receive clean typed data.
The discipline required is keeping business rules out of the schemas; a validation package that
starts querying is a validation package that can no longer be shared.
## Alternatives considered
class-validator + class-transformer, the NestJS default — rejected: not shareable
with the frontends. Duplicating rules with a test to keep them in sync — rejected: the test
tells you about drift after it has already shipped.