@voznov/zod-dto-nestjs 0.3.0 → 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 +26 -20
- package/dist/index.cjs +10 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -124,9 +124,33 @@ Method decorators that parse the return value of a controller (or any class) met
|
|
|
124
124
|
- **`@ZodSerialize`** — runtime parsing only. Use on services, repositories, internal methods.
|
|
125
125
|
- **`@ZodResponse`** — `@ZodSerialize` + auto-emit `@ApiResponse` Swagger metadata (and register inner DTOs via `@ApiExtraModels`). Use on controller routes.
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
`ZodDtoSerializationError extends ZodDtoValidationError` — wire up one exception filter to split client errors (400) from server bugs (500):
|
|
128
128
|
|
|
129
|
-
|
|
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>'`).
|
|
130
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.
|
|
131
155
|
|
|
132
156
|
```ts
|
|
@@ -184,24 +208,6 @@ Both decorators accept the full `ToDtoOptions` bag (`preprocessors`, `observers`
|
|
|
184
208
|
async create(): Promise<NoteDto> { /* ... */ }
|
|
185
209
|
```
|
|
186
210
|
|
|
187
|
-
### Differentiating client errors from server errors
|
|
188
|
-
|
|
189
|
-
`ZodDtoSerializationError extends ZodDtoValidationError`, so a single exception filter can split request-validation failures (client → 400) from response-validation failures (server → 500):
|
|
190
|
-
|
|
191
|
-
```ts
|
|
192
|
-
import { ZodDtoValidationError } from '@voznov/zod-dto';
|
|
193
|
-
import { ZodDtoSerializationError } from '@voznov/zod-dto-nestjs';
|
|
194
|
-
|
|
195
|
-
@Catch(ZodDtoValidationError)
|
|
196
|
-
export class ZodExceptionFilter implements ExceptionFilter {
|
|
197
|
-
catch(error: ZodDtoValidationError, host: ArgumentsHost) {
|
|
198
|
-
const isServerBug = error instanceof ZodDtoSerializationError;
|
|
199
|
-
const status = isServerBug ? 500 : 400;
|
|
200
|
-
// log, format, respond...
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
211
|
## API
|
|
206
212
|
|
|
207
213
|
| Export | Description |
|
package/dist/index.cjs
CHANGED
|
@@ -1388,10 +1388,19 @@ var wrapMethod = (schema, options, decoratorName) => (target, methodName, descri
|
|
|
1388
1388
|
}[methodName];
|
|
1389
1389
|
redecorateFromReflect(originalMethod, descriptor.value);
|
|
1390
1390
|
};
|
|
1391
|
+
var API_RESPONSE_KEY = "swagger/apiResponse";
|
|
1391
1392
|
var wrapApiResponse = (schema, options) => (target, propertyKey, descriptor) => {
|
|
1392
1393
|
const resolved = resolveSchema(schema, target, propertyKey, "@ZodResponse");
|
|
1394
|
+
const status = options?.status ?? 200;
|
|
1395
|
+
const existing = descriptor.value ? Reflect.getMetadata(API_RESPONSE_KEY, descriptor.value) : void 0;
|
|
1396
|
+
if (existing?.[status]) {
|
|
1397
|
+
console.warn(
|
|
1398
|
+
`@ZodResponse on ${target.constructor.name}.${String(propertyKey)}: another response decorator already set metadata for status ${status} \u2014 they will silent-merge. Remove the duplicate to avoid mixed spec output.`
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1393
1401
|
const { so, innerSchemas } = applySwaggerDecorators(resolved);
|
|
1394
|
-
(
|
|
1402
|
+
const schemaForApi = Object.keys(so).length === 1 && Array.isArray(so.oneOf) && so.oneOf.length === 1 ? so.oneOf[0] : so;
|
|
1403
|
+
(0, import_swagger2.ApiResponse)({ status, description: options?.description, schema: schemaForApi })(target, propertyKey, descriptor);
|
|
1395
1404
|
if (innerSchemas.size > 0) {
|
|
1396
1405
|
const classTarget = typeof target === "function" ? target : target.constructor;
|
|
1397
1406
|
(0, import_swagger2.ApiExtraModels)(...innerSchemas)(classTarget);
|