ADR 0016: Zod Everywhere over class-validator / class-transformer
ADR 0016: Zod Everywhere over class-validator / class-transformer
Section titled “ADR 0016: Zod Everywhere over class-validator / class-transformer”- Status: Accepted
- Date: 2026-04-14
- Deciders: Ideony team
Context
Section titled “Context”Ideony’s NestJS API needs request validation (DTOs), and the mobile app needs client-side schema validation (form inputs, API response parsing). The NestJS default is class-validator + class-transformer (decorator-based, requires reflect-metadata). A runtime-schema alternative that works identically on both BE and FE was evaluated.
Decision
Section titled “Decision”Use Zod throughout: nestjs-zod on the backend (createZodDto() for DTOs, createZodValidationPipe() as the global validation pipe), plain Zod on the frontend (form schemas, API response guards). The packages/validators workspace package holds shared schemas used by both apps. class-validator and class-transformer are not installed.
Consequences
Section titled “Consequences”- Single validation library across BE and FE — same schema syntax, no mental model switch.
packages/validatorspackage shares Zod schemas between API DTOs and mobile form validation, eliminating duplication.- Zod infers TypeScript types from schemas (
z.infer<typeof Schema>) — no separate type declarations needed. createZodValidationPipe()(nestjs-zod) validates at the NestJS pipe layer; validation errors are structured and consistent.- Zod parse errors are structured objects (not class-validator’s decorator error arrays) — easier to format for i18n responses.
nestjs-zodis a third-party NestJS integration; must track compatibility with NestJS major upgrades.- Zod v3 schema composition (
.merge(),.extend()) requires care with discriminated unions and optional fields; learning curve for devs familiar with class-validator decorators. - No decorator syntax — DTO classes are thinner wrappers around Zod schemas, which some NestJS developers find unfamiliar.
Alternatives considered
Section titled “Alternatives considered”- class-validator + class-transformer — rejected: decorator-based, requires
reflect-metadata, verbose for complex nested schemas, no easy FE reuse, harder to compose programmatically. - Yup — rejected: weaker TypeScript inference than Zod; less ergonomic API for complex schemas.
- Valibot — considered: smaller bundle, similar API; Zod chosen for ecosystem maturity and
nestjs-zodintegration at decision date. - io-ts — rejected: functional/algebraic style; steep learning curve; less ergonomic than Zod for typical CRUD DTOs.