
Product
A New Design for GitHub PR Comments
We redesigned our GitHub PR comments to deliver clear, actionable security insights without adding noise to your workflow.
nestjs-zod
Advanced tools
✨ A seamless validation solution for your NestJS application ✨
createZodDto
- create DTO classes from Zod schemasZodValidationPipe
- validate body
/ query
/ params
using Zod DTOsZodGuard
- guard routes by validating body
/ query
/ params
UseZodGuard
- alias for @UseGuards(new ZodGuard(source, schema))
ZodValidationException
- BadRequestException extended with Zod errorszodToOpenAPI
- create OpenAPI declarations from Zod schemas@nestjs/swagger
integration using the patchzodToOpenAPI
- generate highly accurate Swagger Schema@nestjs/swagger
decorator@nest-zod/z
)
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to use zod
directly. See MIGRATION.md for more information.dateString
for dates (supports casting to Date
)password
for passwords (more complex string rules + OpenAPI conversion)nestjs-zod/frontend
)npm install nestjs-zod zod
Peer dependencies:
zod
- >= 3.14.3
@nestjs/common
- >= 8.0.0
(required on server side)@nestjs/core
- >= 8.0.0
(required on server side)@nestjs/swagger
- >= 5.0.0
(only when using patchNestJsSwagger
)All peer dependencies are marked as optional for better client side usage, but you need to install required ones when using nestjs-zod
on server side.
import { createZodDto } from 'nestjs-zod'
import { z } from 'zod'
const CredentialsSchema = z.object({
username: z.string(),
password: z.string(),
})
// class is required for using DTO as a type
class CredentialsDto extends createZodDto(CredentialsSchema) {}
DTO does two things:
ZodValidationPipe
@Controller('auth')
class AuthController {
// with global ZodValidationPipe (recommended)
async signIn(@Body() credentials: CredentialsDto) {}
async signIn(@Param() signInParams: SignInParamsDto) {}
async signIn(@Query() signInQuery: SignInQueryDto) {}
// with route-level ZodValidationPipe
@UsePipes(ZodValidationPipe)
async signIn(@Body() credentials: CredentialsDto) {}
}
// with controller-level ZodValidationPipe
@UsePipes(ZodValidationPipe)
@Controller('auth')
class AuthController {
async signIn(@Body() credentials: CredentialsDto) {}
}
import { createZodDto } from 'nestjs-zod/dto'
The validation pipe uses your Zod schema to parse data from parameter decorator.
When the data is invalid - it throws ZodValidationException.
import { ZodValidationPipe } from 'nestjs-zod'
import { APP_PIPE } from '@nestjs/core'
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ZodValidationPipe,
},
],
})
export class AppModule {}
import { ZodValidationPipe } from 'nestjs-zod'
// controller-level
@UsePipes(ZodValidationPipe)
class AuthController {}
class AuthController {
// route-level
@UsePipes(ZodValidationPipe)
async signIn() {}
}
Also, you can instantly pass a Schema or DTO:
import { ZodValidationPipe } from 'nestjs-zod'
import { UserDto, UserSchema } from './auth.contracts'
// using schema
@UsePipes(new ZodValidationPipe(UserSchema))
// using DTO
@UsePipes(new ZodValidationPipe(UserDto))
class AuthController {}
class AuthController {
// the same applies to route-level
async signIn() {}
}
import { createZodValidationPipe } from 'nestjs-zod'
const MyZodValidationPipe = createZodValidationPipe({
// provide custom validation exception factory
createValidationException: (error: ZodError) =>
new BadRequestException('Ooops'),
})
Sometimes, we need to validate user input before specific Guards. We can't use Validation Pipe since NestJS Pipes are always executed after Guards.
The solution is ZodGuard
. It works just like ZodValidationPipe
, except for that is doesn't transform the input.
It has 2 syntax forms:
@UseGuards(new ZodGuard('body', CredentialsSchema))
@UseZodGuard('body', CredentialsSchema)
Parameters:
'body' | 'query' | 'params'
ZodValidationPipe
)When the data is invalid - it throws ZodValidationException.
import { ZodGuard } from 'nestjs-zod'
// controller-level
@UseZodGuard('body', CredentialsSchema)
@UseZodGuard('params', CredentialsDto)
class MyController {}
class MyController {
// route-level
@UseZodGuard('query', CredentialsSchema)
@UseZodGuard('body', CredentialsDto)
async signIn() {}
}
import { createZodGuard } from 'nestjs-zod'
const MyZodGuard = createZodGuard({
// provide custom validation exception factory
createValidationException: (error: ZodError) =>
new BadRequestException('Ooops'),
})
If you don't like ZodGuard
and ZodValidationPipe
, you can use validate
function:
import { validate } from 'nestjs-zod'
validate(wrongThing, UserDto, (zodError) => new MyException(zodError)) // throws MyException
const validatedUser = validate(
user,
UserDto,
(zodError) => new MyException(zodError)
) // returns typed value when succeed
The default server response on validation error looks like that:
{
"statusCode": 400,
"message": "Validation failed",
"errors": [
{
"code": "too_small",
"minimum": 8,
"type": "string",
"inclusive": true,
"message": "String must contain at least 8 character(s)",
"path": ["password"]
}
]
}
The reason of this structure is default ZodValidationException
.
You can customize the exception by creating custom nestjs-zod
entities using the factories:
You can create ZodValidationException
manually by providing ZodError
:
const exception = new ZodValidationException(error)
Also, ZodValidationException
has an additional API for better usage in NestJS Exception Filters:
@Catch(ZodValidationException)
export class ZodValidationExceptionFilter implements ExceptionFilter {
catch(exception: ZodValidationException) {
exception.getZodError() // -> ZodError
}
}
To ensure that a response conforms to a certain shape, you may use the ZodSerializerInterceptor
interceptor.
This would be especially useful in prevent accidental data leaks.
This is similar to NestJs' @ClassSerializerInterceptor
feature here
@ZodSerializerInterceptor
in application root@Module({
...
providers: [
...,
{ provide: APP_INTERCEPTOR, useClass: ZodSerializerInterceptor },
],
})
export class AppModule {}
@ZodSerializerDto
to define the shape of the response for endpoint in controllerconst UserSchema = z.object({ username: string() })
export class UserDto extends createZodDto(UserSchema) {}
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@ZodSerializerDto(UserDto)
getUser(id: number) {
return this.userService.findOne(id) // --> The native service method returns { username: string, password: string by default }
}
}
In the above example, despite the userService.findOne
method returns password
, the password
property will be stripped out thanks to the @ZodSerializerDto
decorator.
ZodSerializationException
You can catch serialization errors using ZodSerializationException
and log them using your preferred logger.
if (exception instanceof ZodSerializationException) {
const zodError = exception.getZodError();
this.logger.error(`ZodSerializationException: ${zodError.message}`);
}
See the example app here for more information.
[!CAUTION]
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
@nest-zod/z
provides a special version of Zod. It helps you to validate the user input more accurately by using our custom schemas and methods.
[!CAUTION]
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
In HTTP, we always accept Dates as strings. But default Zod only has validations for full date-time strings. ZodDateString
was created to address this issue.
// 1. Expect user input to be a "string" type
// 2. Expect user input to be a valid date (by using new Date)
z.dateString()
// Cast to Date instance
// (use it on end of the chain, but before "describe")
z.dateString().cast()
// Expect string in "full-date" format from RFC3339
z.dateString().format('date')
// [default format]
// Expect string in "date-time" format from RFC3339
z.dateString().format('date-time')
// Expect date to be the past
z.dateString().past()
// Expect date to be the future
z.dateString().future()
// Expect year to be greater or equal to 2000
z.dateString().minYear(2000)
// Expect year to be less or equal to 2025
z.dateString().maxYear(2025)
// Expect day to be a week day
z.dateString().weekDay()
// Expect year to be a weekend
z.dateString().weekend()
Valid date
format examples:
2022-05-15
Valid date-time
format examples:
2022-05-02:08:33Z
2022-05-02:08:33.000Z
2022-05-02:08:33+00:00
2022-05-02:08:33-00:00
2022-05-02:08:33.000+00:00
Errors:
invalid_date_string
- invalid date
invalid_date_string_format
- wrong format
Payload:
expected
- 'date' | 'date-time'
invalid_date_string_direction
- not past/future
Payload:
expected
- 'past' | 'future'
invalid_date_string_day
- not weekDay/weekend
Payload:
expected
- 'weekDay' | 'weekend'
too_small
with type === 'date_string_year'
too_big
with type === 'date_string_year'
[!CAUTION]
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
ZodPassword
is a string-like type, just like the ZodDateString
. As you might have guessed, it's intended to help you with password schemas definition.
Also, ZodPassword
has a more accurate OpenAPI conversion, comparing to regular .string()
: it has password
format and generated RegExp string for pattern
.
// Expect user input to be a "string" type
z.password()
// Expect password length to be greater or equal to 8
z.password().min(8)
// Expect password length to be less or equal to 100
z.password().max(100)
// Expect password to have at least one digit
z.password().atLeastOne('digit')
// Expect password to have at least one lowercase letter
z.password().atLeastOne('lowercase')
// Expect password to have at least one uppercase letter
z.password().atLeastOne('uppercase')
// Expect password to have at least one special symbol
z.password().atLeastOne('special')
Errors:
invalid_password_no_digit
invalid_password_no_lowercase
invalid_password_no_uppercase
invalid_password_no_special
too_small
with type === 'password'
too_big
with type === 'password'
[!CAUTION]
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
Created for
nestjs-zod-prisma
z.json()
[!CAUTION]
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
Created for custom schemas in
nestjs-zod-prisma
Just returns the same Schema
z.from(MySchema)
[!CAUTION]
@nest-zod/z
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
Currently, we use custom
error code due to some Zod limitations (errorMap
priorities)
Therefore, the error details is located inside params
property:
const error = {
code: 'custom',
message: 'Invalid date, expected it to be the past',
params: {
isNestJsZod: true,
code: 'invalid_date_string_direction',
// payload is always located here in a flat view
expected: 'past',
},
path: ['date'],
}
[!CAUTION]
@nest-zod/z/frontend
is deprecated and will not be supported soon. It is recommended to usezod
directly. See MIGRATION.md for more information.
Optionally, you can install @nest-zod/z
on the client side.
The library provides you a @nest-zod/z/frontend
entry point, that can be used to detect custom NestJS Zod issues and process them the way you want.
import { isNestJsZodIssue, NestJsZodIssue, ZodIssue } from '@nest-zod/z/frontend'
function mapToFormErrors(issues: ZodIssue[]) {
for (const issue of issues) {
if (isNestJsZodIssue(issue)) {
// issue is NestJsZodIssue
}
}
}
:warning: If you use
zod
in your client-side application, and you want to install@nest-zod/z
too, it may be better to completely switch to@nest-zod/z
to prevent issues caused by mismatch betweenzod
versions.@nest-zod/z/frontend
doesn't usezod
at the runtime, but it uses its types.
Prerequisites:
@nestjs/swagger
with version ^5.0.0
installedApply the patch patchNestJsSwagger()
in your main.ts
file before setting up your swagger module:
import { patchNestJsSwagger } from 'nestjs-zod'
patchNestJsSwagger()
For addtional documentation, follow the Nest.js' Swagger Module Guide, or you can see the example application guide here .
Use .describe()
method to add Swagger description:
import { z } from 'zod'
const CredentialsSchema = z.object({
username: z.string().describe('This is an username'),
password: z.string().describe('This is a password'),
})
You can convert any Zod schema to an OpenAPI JSON object:
import { zodToOpenAPI } from 'nestjs-zod'
import { z } from 'zod'
const SignUpSchema = z.object({
username: z.string().min(8).max(20),
password: z.string().min(8).max(20),
sex: z
.enum(['male', 'female', 'nonbinary'])
.describe('We respect your gender choice'),
social: z.record(z.string().url())
})
const openapi = zodToOpenAPI(SignUpSchema)
The output will be the following:
{
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 8,
"maxLength": 20
},
"password": {
"type": "string",
"minLength": 8,
"maxLength": 20
},
"sex": {
"description": "We respect your gender choice",
"type": "string",
"enum": ["male", "female", "nonbinary"]
},
"social": {
"type": "object",
"additionalProperties": {
"type": "string",
"format": "uri"
}
},
"birthDate": {
"type": "string",
"format": "date-time"
}
},
"required": ["username", "password", "sex", "social", "birthDate"]
}
zod-dto
nestjs-zod
includes a lot of refactored code from zod-dto
.
zod-nestjs and zod-openapi
These libraries bring some new features compared to zod-dto
.
nestjs-zod
has used them too.
FAQs
All NestJS + Zod utilities you need
The npm package nestjs-zod receives a total of 69,344 weekly downloads. As such, nestjs-zod popularity was classified as popular.
We found that nestjs-zod demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
We redesigned our GitHub PR comments to deliver clear, actionable security insights without adding noise to your workflow.
Product
Our redesigned Repositories page adds alert severity, filtering, and tabs for faster triage and clearer insights across all your projects.
Security News
Slopsquatting is a new supply chain threat where AI-assisted code generators recommend hallucinated packages that attackers register and weaponize.