ADR-0016: Validation Strategy — class-validator
Date: 2026-07-29 Status: Accepted
Context
The NestJS backend uses class-validator + class-transformer for request validation, integrated via a custom decorator system (WalnutAdminDecoratorField*). The frontend has no runtime schema validation layer.
Industry consensus (see 行业调研:Vue3 + NestJS 全栈架构) favors Zod for fullstack monorepos: a single schema serves both backend validation and frontend form validation, with automatic TypeScript type inference (z.infer<typeof schema>).
Two paths exist:
- Migrate to Zod — replace
class-validatorDTOs with Zod schemas, addZodValidationPipein NestJS - Stay with class-validator — keep existing decorator system, acknowledge the trade-off
Decision
Chosen: Stay with class-validator + custom decorator system. Do not introduce Zod at this time.
Rationale
The existing decorator system already provides "single declaration" semantics.
WalnutAdminDecoratorField*decorators combine validation (class-validator) + transformation (class-transformer) + Swagger documentation into one decorator. This achieves a similar DX to Zod schemas (one declaration = validation + types + docs).Migration cost is disproportionate to current benefit. The validation system is deeply embedded across the codebase:
- 16 guard classes depend on the DTO/validation pipeline
- Middleware and interceptor layers expect
class-validatorerror formats libs/decorators/contains 6 sub-systems (field/params/query/transformer/validator/swagger) with ~30 decorator files- All existing DTOs (~100+ classes) would need rewriting
Frontend form validation reuse is not yet a pain point. The primary benefit of Zod (same schema on frontend and backend) requires frontend form libraries (VeeValidate + Zod). Without this integration, Zod on the backend alone adds dependency overhead without the "DRY validation" payoff.
Zod introduces a second validation paradigm. Even a gradual migration means maintaining two validation systems side-by-side —
class-validatorfor existing modules, Zod for new ones. This bifurcation increases cognitive load for contributors.
Trigger Conditions for Re-evaluation
Revisit this decision when any of these conditions are met:
| Trigger | Why it changes the calculus |
|---|---|
| Frontend adopts VeeValidate + Zod for form validation | DRY validation across frontend and backend becomes achievable |
| A new greenfield NestJS module is created from scratch | Opportunity to try Zod without migration cost |
class-validator becomes unmaintained or incompatible with a NestJS major version | Forced migration |
| The project grows to 3+ frontend apps sharing validation with the same backend | Shared schema ROI increases |
Alternatives considered
- Migrate to Zod (replace
class-validatorDTOs with Zod schemas, add aZodValidationPipein NestJS) —— the migration cost is disproportionate to the current benefit. TheWalnutAdminDecoratorField*decorators already fold validation (class-validator), transformation (class-transformer) and Swagger documentation into a single declaration; 16 guard classes plus the middleware / interceptor layers depend on the existing pipeline's error format; all 6 sub-systems underlibs/decorators/(~30 decorator files) and ~100+ DTO classes would have to be rewritten. And because the frontend has not adopted VeeValidate + Zod, the "one schema shared by frontend and backend" payoff is not reachable — Zod on the backend alone only adds a second validation paradigm.
Consequences
- Validation logic remains backend-only (no frontend schema reuse)
- DTO files continue using
class-validatordecorators +RealPickType/RealPartialTypehelpers - New NestJS modules follow the existing
WalnutAdminDecoratorField*pattern - All existing
libs/decorators/infrastructure is preserved - If a trigger condition is met, start with a single module as proof-of-concept before committing to full migration
Related
- Zod vs class-validator 评估 — detailed Zod vs class-validator comparison
- 行业调研:Vue3 + NestJS 全栈架构 — industry recommendation for Zod in fullstack monorepos
apps/server/libs/decorators/— current class-validator decorator systemapps/server/AGENTS.md— DTO design rules (RealPickType, field decorator usage)