Zod DTO可选字段在Swagger中误显示为必填的问题
问题详情
我定义了如下Zod Schema与DTO类:
const FinesListPaginatedQuerySchema = z.object({ dateFrom: z.coerce.date().optional()}) export class FinesListPaginatedRequestQueryDto extends createZodDto(FinesListPaginatedQuerySchema) {}
控制器代码:
public findFines(@Query() query: FinesListPaginatedRequestQueryDto))
预期dateFrom字段在Swagger中显示为可选,但实际是必填项。
补充情况:当Schema同时包含必填与可选字段时,Swagger显示正常;但所有字段均为可选时,这些字段都会被标记为必填。排查发现,执行const reflectedParam = Reflect.getMetadata(constants_1.DECORATORS.API_MODEL_PROPERTIES, prototype, key)返回的参数缺少required选项(仅当所有参数为可选时该值为undefined)。
解决方案
1. 手动添加@ApiProperty标记可选
直接在DTO的字段上手动指定required: false,强制Swagger识别为可选:
import { ApiProperty } from '@nestjs/swagger'; import { createZodDto } from 'nestjs-zod'; import { z } from 'zod'; const FinesListPaginatedQuerySchema = z.object({ dateFrom: z.coerce.date().optional() }); export class FinesListPaginatedRequestQueryDto extends createZodDto(FinesListPaginatedQuerySchema) { @ApiProperty({ required: false, type: String, format: 'date' }) dateFrom?: Date; }
2. 修复元数据生成逻辑
如果是nestjs-zod库在处理全可选Schema时未正确生成元数据,可以自定义扩展函数补全:
import { createZodDto } from 'nestjs-zod'; import { DECORATORS } from '@nestjs/swagger/dist/constants'; import { Reflect } from 'reflect-metadata'; import { z } from 'zod'; export function createCustomZodDto<T extends z.ZodTypeAny>(schema: T) { const DtoClass = createZodDto(schema); Object.keys(schema.shape).forEach(key => { const fieldSchema = schema.shape[key]; const isRequired = !fieldSchema.isOptional(); const existingMeta = Reflect.getMetadata(DECORATORS.API_MODEL_PROPERTIES, DtoClass.prototype, key) || {}; Reflect.defineMetadata( DECORATORS.API_MODEL_PROPERTIES, { ...existingMeta, required: isRequired }, DtoClass.prototype, key ); }); return DtoClass; } // 使用自定义函数创建DTO export class FinesListPaginatedRequestQueryDto extends createCustomZodDto(FinesListPaginatedQuerySchema) {}
3. 临时 workaround:添加隐藏必填字段
如果不想修改现有逻辑,可临时加一个不会被业务使用的“伪必填”字段,触发Swagger的正常识别:
const FinesListPaginatedQuerySchema = z.object({ dateFrom: z.coerce.date().optional(), // 仅用于触发Swagger元数据生成,实际不会被使用 _dummy: z.string().optional().default('').transform(() => undefined) });
注:此方法为临时方案,优先推荐前两种正规解决方式。
内容的提问来源于stack exchange,提问作者noChechako
相关产品推荐
相关产品推荐

