Integrate Zod and Open API into Nest.js Prisma project
Published: 2023-10-04 05:31:09

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.

```JavaScript
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) {}

```

To enable zod validation, add the validation pipe into `app.module.ts`.
```JavaScript
@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_PIPE,
      useClass: ZodValidationPipe,
    },
  ],
})
export class AppModule {}
```

## 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`.

```Prisma
generator zod {
  provider         = "zod-prisma-types"
  output           = "../src/generated/zod"
  createInputTypes = false
}
```

You will get generated schemas like this.

```JavaScript
export const UserSchema = z.object({
  id: z.number().int(),
  email: z.string(),
  name: z.string().nullable(),
})
```

## Integrate Zod into Open API

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

```JavaScript
  @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);
    }
  }
```

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

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zz3mrnm5couzrdy11bnl.png" width="30%">

Let us see if the validation works.
Sending an invalid json to `POST users`,
```
{
  "email": "alice email",
  "name": "alice"
}
```
we get the following 400 error.
```
{
  "statusCode": 400,
  "message": "Validation failed",
  "errors": [
    {
      "validation": "email",
      "code": "invalid_string",
      "message": "Invalid email",
      "path": [
        "email"
      ]
    }
  ]
}
```

Here is another example.
The `users/:id/posts` endpoint which returns user's posts.
```JavaScript
  @Get(':id/posts')
  @ApiOkResponse({
    schema: zodToOpenAPI(z.array(PostSchema)),
  })
  async getPosts(@Param('id') id: number) {
    return await this.usersService.getPosts(id);
  }

```

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

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/by8ufa7lg1dqbmi4hz8e.png" width="30%">

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