Skip to content

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

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.

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.

  • Single validation library across BE and FE — same schema syntax, no mental model switch.
  • packages/validators package 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-zod is 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.
  • 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-zod integration at decision date.
  • io-ts — rejected: functional/algebraic style; steep learning curve; less ergonomic than Zod for typical CRUD DTOs.