DEV Community

ikeda1729
ikeda1729

Posted on

Integrate Zod and Open API into Nest.js Prisma project

In this article, I will show how to use Zod and Open API (Swagger) in a Nest.js, Prisma project.
I made a github repo for this example.
https://github.com/ikeda1729/nestjs-prisma-zod

Using Zod in Nest.js

I use a useful package nestjs-zod.
https://github.com/risen228/nestjs-zod

With nestjs-zod, a dto is simply written like this.

import { createZodDto } from 'nestjs-zod'
import { z } from 'nestjs-zod/z'

export const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string(),
})

export class CreateUserDto extends createZodDto(CreateUserSchema) {}

Enter fullscreen mode Exit fullscreen mode

To enable zod validation, add the validation pipe into app.module.ts.

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_PIPE,
      useClass: ZodValidationPipe,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Using Zod with Prisma

I use a useful package zod-prisma-types.
This package generates zod schemas from a prisma schema file.

Add generator in the prisma schema and run npx prisma generate.

generator zod {
  provider         = "zod-prisma-types"
  output           = "../src/generated/zod"
  createInputTypes = false
}
Enter fullscreen mode Exit fullscreen mode

You will get generated schemas like this.

export const UserSchema = z.object({
  id: z.number().int(),
  email: z.string(),
  name: z.string().nullable(),
})
Enter fullscreen mode Exit fullscreen mode

Integrate Zod into Open API

We can integrate zod schemas using zodToOpenApi.
This POST users endpoint simply returns a prisma user entity.

  @Post()
  @ApiBody({
    schema: zodToOpenAPI(CreateUserSchema),
  })
  @ApiOkResponse({
    schema: zodToOpenAPI(UserSchema),
  })
  async create(@Body() createUserDto: CreateUserDto) {
    try {
      return await this.usersService.create(createUserDto);
    } catch (e: any) {
      if (e.code === 'P2002')
        throw new HttpException('User already exists', 409);
    }
  }
Enter fullscreen mode Exit fullscreen mode

The Open API doc looks like this.
The request body and the response body are displayed according to zod schemas.

Let us see if the validation works.
Sending an invalid json to POST users,

{
  "email": "alice email",
  "name": "alice"
}
Enter fullscreen mode Exit fullscreen mode

we get the following 400 error.

{
  "statusCode": 400,
  "message": "Validation failed",
  "errors": [
    {
      "validation": "email",
      "code": "invalid_string",
      "message": "Invalid email",
      "path": [
        "email"
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Here is another example.
The users/:id/posts endpoint which returns user's posts.

  @Get(':id/posts')
  @ApiOkResponse({
    schema: zodToOpenAPI(z.array(PostSchema)),
  })
  async getPosts(@Param('id') id: number) {
    return await this.usersService.getPosts(id);
  }

Enter fullscreen mode Exit fullscreen mode

We see the response body is the array of the post entity.

Conclusion

With nestjs-zod and zod-prisma-types, we can easily integrate Zod into Nest.js Prisma projects.

Top comments (3)

Collapse
 
laptiev profile image
Dmytro

Why don't you wanna use class-validator?

Collapse
 
ikeda1729 profile image
ikeda1729

class-validator is OK too, of course.
I feel like it's redundant to do write @ApiProperty on each field.

Collapse
 
ikeda1729 profile image
ikeda1729

Additionally, it is convenient to generate zod shcemas from a prisma schema.