@voznov/zod-dto-nestjs 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -117,12 +117,108 @@ app.useGlobalPipes(
117
117
  );
118
118
  ```
119
119
 
120
+ ## Response validation — `@ZodSerialize` / `@ZodResponse`
121
+
122
+ Method decorators that parse the return value of a controller (or any class) method through a Zod schema. If the method returns something that doesn't match the schema, a `ZodDtoSerializationError` is thrown — caught at runtime *before* the value reaches the client, so server-side bugs are surfaced as 500s instead of leaking malformed payloads or extra fields.
123
+
124
+ - **`@ZodSerialize`** — runtime parsing only. Use on services, repositories, internal methods.
125
+ - **`@ZodResponse`** — `@ZodSerialize` + auto-emit `@ApiResponse` Swagger metadata (and register inner DTOs via `@ApiExtraModels`). Use on controller routes.
126
+
127
+ `ZodDtoSerializationError extends ZodDtoValidationError` — wire up one exception filter to split client errors (400) from server bugs (500):
128
+
129
+ ```ts
130
+ import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common';
131
+ import { ZodDtoValidationError } from '@voznov/zod-dto';
132
+ import { ZodDtoSerializationError } from '@voznov/zod-dto-nestjs';
133
+
134
+ @Catch(ZodDtoValidationError)
135
+ export class ZodExceptionFilter implements ExceptionFilter {
136
+ catch(error: ZodDtoValidationError, host: ArgumentsHost) {
137
+ const isServerBug = error instanceof ZodDtoSerializationError;
138
+ const status = isServerBug ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.BAD_REQUEST;
139
+ host.switchToHttp().getResponse().status(status).json({
140
+ statusCode: status,
141
+ message: error.message,
142
+ issues: isServerBug ? undefined : error.issues,
143
+ });
144
+ }
145
+ }
146
+
147
+ // main.ts
148
+ app.useGlobalFilters(new ZodExceptionFilter());
149
+ ```
150
+
151
+ Both decorators come in two overloads:
152
+
153
+ - **Strict** — schema passed explicitly. The method's return type is constrained at compile time to match the schema's output; `tsc` errors on mismatch as `TS1241: Unable to resolve signature of method decorator...` — the actual mismatch is on the deepest line of the message (`Type 'X' is not assignable to type 'NoteDto | Promise<NoteDto>'`).
154
+ - **Loose** — no schema. Resolves from `design:returntype` metadata at runtime (`: NoteDto` annotation suffices). No compile-time check; doesn't work on generic return types (`NoteDto[]`, `Promise<NoteDto>`, unions) since TypeScript erases generics in metadata — pass the schema explicitly in that case.
155
+
156
+ ```ts
157
+ import { Body, Controller, Get, Param, Post } from '@nestjs/common';
158
+ import { ZodResponse } from '@voznov/zod-dto-nestjs';
159
+ import { z } from 'zod';
160
+
161
+ @Controller('notes')
162
+ export class NotesController {
163
+ // Strict + auto-Swagger: tsc enforces the return type, spec gets `$ref` to NoteDto.
164
+ @Get(':id')
165
+ @ZodResponse(NoteDto)
166
+ findOne(@Param() p: NoteIdParam): NoteDto { /* ... */ }
167
+
168
+ // Async + array: tsc enforces `Promise<NoteDto[]>`. Generics erased in design:returntype,
169
+ // so the schema must be passed explicitly here.
170
+ @Get()
171
+ @ZodResponse(z.array(NoteDto))
172
+ async list(): Promise<NoteDto[]> { /* ... */ }
173
+
174
+ // Override status (default 200) + description for the OpenAPI operation.
175
+ @Post()
176
+ @ZodResponse(NoteDto, { status: 201, description: 'note created' })
177
+ create(@Body() body: CreateNoteDto): NoteDto { /* ... */ }
178
+ }
179
+ ```
180
+
181
+ Runtime-only sibling for layers below the controller — same overloads, no Swagger emission:
182
+
183
+ ```ts
184
+ import { ZodSerialize } from '@voznov/zod-dto-nestjs';
185
+
186
+ class NotesService {
187
+ // Throws ZodDtoSerializationError if the return shape doesn't match.
188
+ @ZodSerialize(NoteDto)
189
+ findOne(id: string): NoteDto { /* ... */ }
190
+
191
+ // Loose: schema resolved from `design:returntype` (NoteDto class). Won't work for `Promise<...>` / `NoteDto[]` — generic erased to `Promise` / `Array`. Use the strict overload for those.
192
+ @ZodSerialize()
193
+ default(): NoteDto { /* ... */ }
194
+ }
195
+ ```
196
+
197
+ ### Options
198
+
199
+ Both decorators accept the full `ToDtoOptions` bag (`preprocessors`, `observers`, `errorClass` — same semantics as [`toDto.with`](https://www.npmjs.com/package/@voznov/zod-dto), but applied to the method's *return* value instead of an input). The default `errorClass` is `ZodDtoSerializationError` (vs `ZodDtoValidationError` for `toDto`), so an exception filter can split client errors from server bugs (see below).
200
+
201
+ `@ZodResponse` extends the bag with two Swagger-only fields:
202
+
203
+ - `status: number` — HTTP status for the OpenAPI response object. Default `200`.
204
+ - `description: string` — description on the OpenAPI response object.
205
+
206
+ ```ts
207
+ @ZodResponse(NoteDto, { status: 201, observers: [(note) => metrics.recordCreate(note)] })
208
+ async create(): Promise<NoteDto> { /* ... */ }
209
+ ```
210
+
120
211
  ## API
121
212
 
122
- | Export | Description |
123
- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
124
- | `ZodValidationPipe` | `PipeTransform` for `@Body()` / `@Param()` / `@Query()`. Accepts `{ createError?: (issues: string[]) => Error }`. |
125
- | `applySwaggerDecorators(schema)` | Low-level: apply `@ApiProperty` metadata to a schema. Auto-invoked via `registerOnCreate`; export is for manual/edge-case use. |
213
+ | Export | Description |
214
+ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
215
+ | `ZodValidationPipe` | `PipeTransform` for `@Body()` / `@Param()` / `@Query()`. Accepts `{ createError?: (issues: string[]) => Error }`. |
216
+ | `ZodValidationPipeOptions` | Options type for `ZodValidationPipe`. |
217
+ | `ZodSerialize(schema?, options?)` | Method decorator: runtime-parse the return value through `schema` (or via `design:returntype` if omitted). Throws `ZodDtoSerializationError` on mismatch. |
218
+ | `ZodResponse(schema?, options?)` | `ZodSerialize` + auto-emits `@ApiResponse` Swagger metadata (and `@ApiExtraModels` for inner DTOs). |
219
+ | `ZodResponseOptions` | Options type for `ZodResponse` (`ToDtoOptions & { status?, description? }`). |
220
+ | `ZodDtoSerializationError` | Subclass of `ZodDtoValidationError` thrown by `@ZodSerialize` / `@ZodResponse` when a method returns an invalid shape. |
221
+ | `applySwaggerDecorators(schema)` | Low-level: apply `@ApiProperty` metadata to a schema. Auto-invoked via `registerOnCreate`; export is for manual/edge-case use. |
126
222
 
127
223
  ## License
128
224