diff --git a/README.md b/README.md index 867e29f..b17519c 100755 --- a/README.md +++ b/README.md @@ -348,6 +348,18 @@ export class UserController { In the above example, despite the `userService.findOne` method returns `password`, the `password` property will be stripped out thanks to the `@ZodSerializerDto` decorator. +### Logging serialization errors using `ZodSerializationException` + +You can catch serialization errors using `ZodSerializationException` and log them using your preferred logger. + +```ts +if (exception instanceof ZodSerializationException) { + const zodError = exception.getZodError(); + this.logger.error(`ZodSerializationException: ${zodError.message}`); +} +``` +See the example app [here](/packages/example/src/http-exception.filter.ts) for more information. + ## Extended Zod > [!CAUTION] diff --git a/packages/example/src/app.module.ts b/packages/example/src/app.module.ts index 3402f9e..e5fa5fe 100644 --- a/packages/example/src/app.module.ts +++ b/packages/example/src/app.module.ts @@ -1,9 +1,10 @@ import { Module } from '@nestjs/common'; -import { ZodValidationPipe } from 'nestjs-zod' -import { APP_PIPE } from '@nestjs/core' +import { ZodSerializerInterceptor, ZodValidationPipe } from 'nestjs-zod' +import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core' import { AppController } from './app.controller'; import { AppService } from './app.service'; import { PostsModule } from './posts/posts.module'; +import { HttpExceptionFilter } from './http-exception.filter'; @Module({ @@ -15,6 +16,14 @@ import { PostsModule } from './posts/posts.module'; provide: APP_PIPE, useClass: ZodValidationPipe, }, + { + provide: APP_INTERCEPTOR, + useClass: ZodSerializerInterceptor, + }, + { + provide: APP_FILTER, + useClass: HttpExceptionFilter, + }, ] }) export class AppModule {} diff --git a/packages/example/src/http-exception.filter.ts b/packages/example/src/http-exception.filter.ts new file mode 100644 index 0000000..862902d --- /dev/null +++ b/packages/example/src/http-exception.filter.ts @@ -0,0 +1,17 @@ +import { Logger, Catch, ArgumentsHost, HttpException } from '@nestjs/common'; +import { BaseExceptionFilter } from '@nestjs/core'; +import { ZodSerializationException } from 'nestjs-zod'; + +@Catch(HttpException) +export class HttpExceptionFilter extends BaseExceptionFilter { + private readonly logger = new Logger(HttpExceptionFilter.name); + + catch(exception: HttpException, host: ArgumentsHost) { + if (exception instanceof ZodSerializationException) { + const zodError = exception.getZodError(); + this.logger.error(`ZodSerializationException: ${zodError.message}`); + } + + super.catch(exception, host); + } +} diff --git a/packages/example/src/posts/posts.controller.ts b/packages/example/src/posts/posts.controller.ts index 70850ba..c49a4d9 100644 --- a/packages/example/src/posts/posts.controller.ts +++ b/packages/example/src/posts/posts.controller.ts @@ -1,6 +1,6 @@ import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { ApiOkResponse } from '@nestjs/swagger'; -import { createZodDto } from 'nestjs-zod' +import { createZodDto, ZodSerializerDto } from 'nestjs-zod' import { z } from 'zod' class PostDto extends createZodDto(z.object({ @@ -23,6 +23,7 @@ export class PostsController { } @Get(':id') + @ZodSerializerDto(PostDto) @ApiOkResponse({ type: PostDto, description: 'Get a post by ID' }) getById(@Param('id') id: string) { return { diff --git a/packages/nestjs-zod/src/index.ts b/packages/nestjs-zod/src/index.ts index 5759a7d..a53c18e 100644 --- a/packages/nestjs-zod/src/index.ts +++ b/packages/nestjs-zod/src/index.ts @@ -1,6 +1,6 @@ export type { ZodDto } from './dto' export { createZodDto } from './dto' -export { ZodValidationException } from './exception' +export { ZodValidationException, ZodSerializationException } from './exception' export { createZodGuard, UseZodGuard, ZodGuard } from './guard' export { patchNestJsSwagger, zodToOpenAPI } from './openapi' export { createZodValidationPipe, ZodValidationPipe } from './pipe'