-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
feat(common): add standard-schema validation pipe #16120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat(common): add standard-schema validation pipe #16120
Conversation
|
Added integration tests with real Zod schemas (10 tests):
All tests pass with Zod v3.24. |
|
I found a bit odd that we need to define the same properties twice (in the class with no metadata & with zod) At same time I don't think that we can address this without losing the class-like approach |
|
Yeah, you're right - there's definitely duplication here. It's a known trade-off when mixing Zod with classes. A couple of options I see:
export class CreateUserDto implements z.infer<typeof createUserSchema> {
static schema = createUserSchema;
declare name: string;
declare email: string;
}
Since this pipe is Standard Schema agnostic, users could also pick TypeBox or Valibot which infer types from schemas more naturally. Want me to update the example to show the |
|
Yes, let's stick with the |
|
Please add an example with nested DTOs and with |
BenLorantfy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @malkovitc @micalevisk, maintainer of nestjs-zod here 👋
I have a few questions about this PR and how 3rd party libraries like nestjs-zod fit into this. I'd like to:
- Make sure
nestjs-zodis aligned with the work being done here - Figure out how
nestjs-zodfits into all this - Understand if
nestjs-zodis needed at all anymore (I think it is, based off my understanding of how zod has additional features built on-top of standard schema)
nestjs-zod has a decent number of users (1 million downloads per month), so I'd like to also figure out how to avoid disruption for them.
Also please note in this issue the possibility of moving nestjs-zod as an official package was brought up: BenLorantfy/nestjs-zod#65. It might be good to resolve this issue so nestjs-zod is aligned with the work being done here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this PR just adds a validation pipe, but I'm curious if there's a plan to add serialization and how that would work? Because I have a few questions about serialization:
- For
zodat-least, consumers likely expectparseto run when serializing the response, sotransform()s are run, extra fields are removed, etc. zodalso has a concept ofcodecs, which I don't think are part of standard schema. If a consumer is using a codec, they likely expectencodeto run when serializing out the response. You can see how nestjs-zod handles this here: https://github.com/BenLorantfy/nestjs-zod?tab=readme-ov-file#codecs
Is serialization going to be a responsibility of external libraries built ontop of this PR?
| export class QueryDto implements z.infer<typeof querySchema> { | ||
| static schema = querySchema; | ||
|
|
||
| @ApiPropertyOptional({ | ||
| description: 'Number of items to return', | ||
| example: 10, | ||
| minimum: 1, | ||
| maximum: 100, | ||
| }) | ||
| declare limit?: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So my biggest problem with class-validator is that it leads you to duplicate information. I wrote about this here: https://www.benlorantfy.com/blog/the-data-triangle-and-nestjs-zod-v5
Is there no way to avoid this? In nestjs-zod we avoid this by using a factory function:
I'm worried about not only the schema field names being duplicated by the class field names, but also other information like description (which might be set by meta({ description: '' }) on the schema) and minimum (which might be set by gt())
I'm also worried consumers might forget to add implements z.infer<typeof createUserSchema>, in which case the duplication is not just extra characters but also caries risk of the type information drifting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea that other 3rd party libraries (like nestjs-zod) would build a factory function on-top of this work, to handle avoiding duplication like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zod also has a function called toJSONSchema that creates json schemas from zod schemas. This is how nestjs-zod is able to use description from meta({ description: '' }) without needing @ApiProperty
toJSONSchema also produces different results based off io: https://zod.dev/json-schema#io .
Is it up to 3rd party libraries, like nestjs-zod to implement _OPENAPI_METADATA_FACTORY ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any plan here to make it so consumers can make reusable / named openapi schemas? See: https://github.com/BenLorantfy/nestjs-zod?tab=readme-ov-file#reusable-schemas
Is this going to be a responsibility of external libraries built onto of this PR?
|
@BenLorantfy hi! Well done! Now we are more into supporting the standard rather than creating a I'm still no sure how to properly support the standard but it seems to be really wanted by the nodejs community |
I think the 2 things have different scopes. Both could be considered |
Why integrate nestjs-zod into the standard pattern? The very reason for pushing this issue is to eliminate these third-party packages. While nestjs-zod is excellent, like class-validator, its developers aren't particularly active in maintaining it. So no matter how rich or robust its features, issues won't be resolved promptly. In production environments, everyone must evaluate whether it's worth introducing. Moreover, the choice of validation approach should remain entirely up to developers—a matter of personal preference. Those seeking standardization can opt for the standard pattern, while those desiring richer functionality can choose nestjs-zod or class-validator. Crucially, nestjs-zod or class-validator should support the standard pattern, not the other way around. |
|
Hi @0x0bit , thanks for your reply
I'm the maintainer and I'm a bit confused by this statement.
I'm not sure what you mean by this?
I don't think I said it should be "the other way around" ? I think we're on the same page here.
@0x0bit these statements are contradictory Anyway, my questions were about figuring out how
And I don't see any handling of these topics in this PR |
Add a new validation pipe that supports Standard Schema-compliant validators
(Zod, Valibot, ArkType, etc.) for request validation.
Features:
- Works with any Standard Schema v1 compliant library
- Zero runtime dependencies - schemas are defined on DTO classes
- Async validation support
- Customizable error HTTP status code
- Custom exception factory support
- Configurable schema property name
Usage:
```typescript
import { z } from 'zod';
class CreateUserDto {
static schema = z.object({
email: z.string().email(),
name: z.string().min(2),
});
email: string;
name: string;
}
app.useGlobalPipes(new StandardSchemaValidationPipe());
```
Closes nestjs#14539
- Add Zod as devDependency for testing - Create integration test suite with real Zod schemas - Test valid/invalid data handling - Test query parameters validation - Test pipe options (disableErrorMessages, errorHttpStatusCode)
7893aed to
93b0ec1
Compare
PR Checklist
PR Type
What is the current behavior?
Issue Number: #14539
NestJS currently relies on
class-validatorandclass-transformerfor validation, which requires decorators and runtime reflection. Many modern validation libraries (Zod, Valibot, ArkType, etc.) now support Standard Schema - a common interface for schema validation.What is the new behavior?
Added
StandardSchemaValidationPipethat works with any Standard Schema v1 compliant library:Features:
Options:
errorHttpStatusCodeErrorHttpStatusCode400exceptionFactory(issues) => anydisableErrorMessagesbooleanfalsevalidateCustomDecoratorsbooleanfalseschemaPropertystring'schema'Does this PR introduce a breaking change?
This is a new feature that doesn't affect existing
ValidationPipebehavior. Users can gradually adopt it for new endpoints while continuing to useValidationPipefor existing code.