You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:47:05