From 63576a75e5ea5fa53005daa62df4b938a5029a4e Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 1 Nov 2024 11:15:22 -0400 Subject: [PATCH 1/2] Export `ZodSerializationException` --- README.md | 12 +++++++++ packages/example/src/app.module.ts | 13 ++++++++-- packages/example/src/http-exception.filter.ts | 25 +++++++++++++++++++ .../example/src/posts/posts.controller.ts | 3 ++- packages/nestjs-zod/src/index.ts | 2 +- 5 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 packages/example/src/http-exception.filter.ts 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..36e7dc3 --- /dev/null +++ b/packages/example/src/http-exception.filter.ts @@ -0,0 +1,25 @@ +import { Logger, ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common'; +import { Request, Response } from 'express'; +import { ZodSerializationException } from 'nestjs-zod'; + +@Catch(HttpException) +export class HttpExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(HttpExceptionFilter.name); + + catch(exception: HttpException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const status = exception.getStatus(); + + if (exception instanceof ZodSerializationException) { + const zodError = exception.getZodError(); + this.logger.error(`ZodSerializationException: ${zodError.message}`); + } + + response + .status(status) + .json({ + statusCode: status + }); + } +} 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' From c2f5d37a76695a97dc584e927f1bb01f3e62c963 Mon Sep 17 00:00:00 2001 From: Ben Date: Fri, 1 Nov 2024 11:23:16 -0400 Subject: [PATCH 2/2] wip --- packages/example/src/http-exception.filter.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/example/src/http-exception.filter.ts b/packages/example/src/http-exception.filter.ts index 36e7dc3..862902d 100644 --- a/packages/example/src/http-exception.filter.ts +++ b/packages/example/src/http-exception.filter.ts @@ -1,25 +1,17 @@ -import { Logger, ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common'; -import { Request, Response } from 'express'; +import { Logger, Catch, ArgumentsHost, HttpException } from '@nestjs/common'; +import { BaseExceptionFilter } from '@nestjs/core'; import { ZodSerializationException } from 'nestjs-zod'; @Catch(HttpException) -export class HttpExceptionFilter implements ExceptionFilter { +export class HttpExceptionFilter extends BaseExceptionFilter { private readonly logger = new Logger(HttpExceptionFilter.name); catch(exception: HttpException, host: ArgumentsHost) { - const ctx = host.switchToHttp(); - const response = ctx.getResponse(); - const status = exception.getStatus(); - if (exception instanceof ZodSerializationException) { const zodError = exception.getZodError(); this.logger.error(`ZodSerializationException: ${zodError.message}`); } - response - .status(status) - .json({ - statusCode: status - }); + super.catch(exception, host); } }