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

使用nestjs-zod时如何在响应中序列化Date且不破坏Swagger

问题

我正在使用nestjs-zod包构建NestJS API,用于请求/响应校验和Swagger生成。我的领域实体如下:

export class UserEntity {
  constructor(
    public readonly id: string,
    public readonly username: string,
    public readonly hashPassword: string,
    public readonly is_active: boolean,
    public readonly created_at: Date,
    public readonly updated_at: Date | null,
  ) {}
}

当前Zod响应schema:

import { z } from "zod"
import { createZodDto } from "nestjs-zod"

export const registerResponseSchema = z.object({
  id: z.string(),
  username: z.string(),
  created_at: z.string(),
})

export class RegisterResponse extends createZodDto(registerResponseSchema) {}

控制器代码:

import { Body, Controller, Post } from '@nestjs/common'
import { ApiOperation, ApiTags } from '@nestjs/swagger'
import { ZodResponse } from 'nestjs-zod'

@ApiTags('Auth')
@Controller('auth')
export class AuthController {
  constructor(private readonly register: RegisterUseCase) {}

  @ApiOperation({ summary: 'Registrations' })
  @ZodResponse({
    status: 201,
    type: RegisterResponse,
  })
  @Post('register')
  async registration(@Body() body: RegisterRequest) {
    return await this.register.register(body.username, body.password)
  }
}

现在遇到的矛盾:

  • 实体返回的created_at是Date类型
  • 若将响应schema定义为created_at: z.string(),Swagger可正常生成,但运行时校验失败(返回值是Date而非字符串)
  • 若改为created_at: z.date(),运行时校验正常,但Swagger生成时会报错:Date cannot be represented in JSON Schema

需要同时满足三个需求:

  1. 使用ZodResponse校验响应
  2. 生成Swagger/OpenAPI文档
  3. 实体中保留Date类型

请问使用nestjs-zod结合Swagger时,处理响应中Date字段的推荐方案是什么?

推荐解决方案

方案1:用Zod强制转换实现自动类型适配

借助Zod的coerce.string()结合transform,实现Date到字符串的自动转换,同时让Swagger识别为string类型:

import { z } from "zod"
import { createZodDto } from "nestjs-zod"

export const registerResponseSchema = z.object({
  id: z.string(),
  username: z.string(),
  // 强制将Date转为ISO字符串,兼容运行时校验与Swagger文档
  created_at: z.coerce.string().transform(val => new Date(val).toISOString()),
})

export class RegisterResponse extends createZodDto(registerResponseSchema) {}

该方案无需修改实体或业务逻辑,Zod会自动完成类型转换与校验,Swagger也会生成符合JSON Schema规范的string类型定义。

方案2:自定义Zod类型适配Swagger规范

利用nestjs-zod的extendApi方法,给Zod的date类型附加Swagger兼容的格式定义:

import { z } from "zod"
import { createZodDto } from "nestjs-zod"
import { extendApi } from "nestjs-zod/dist/swagger"

// 自定义类型:校验Date并转为ISO字符串,同时指定Swagger格式
const dateAsString = z.date().transform(date => date.toISOString())
extendApi(dateAsString, { type: 'string', format: 'date-time' })

export const registerResponseSchema = z.object({
  id: z.string(),
  username: z.string(),
  created_at: dateAsString,
})

export class RegisterResponse extends createZodDto(registerResponseSchema) {}

此方案既保留了Zod对Date类型的运行时校验,又通过扩展Swagger定义解决了JSON Schema不支持Date的问题,文档会显示为string(date-time)类型。

方案3:在业务层提前转换Date为字符串

在UseCase或控制器中,将实体的Date字段转为ISO字符串后再返回,保持Zod schema为z.string():

// 修改RegisterUseCase的register方法
async register(username: string, password: string): Promise<{id: string; username: string; created_at: string}> {
  // 业务逻辑生成UserEntity实例
  const user = new UserEntity(/* 初始化参数 */)
  
  // 转换Date字段为字符串后返回
  return {
    id: user.id,
    username: user.username,
    created_at: user.created_at.toISOString(),
  }
}

此时Zod schema无需修改,运行时校验与Swagger文档生成均可正常工作,实体依然保留Date类型。

内容的提问来源于stack exchange,提问作者Никита Скобелев

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 11:12:38