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

Prisma日期对象无法通过Zod日期校验,求通用解决方案

问题

代码示例

export const getSales = procedure.query(async ({ ctx }) => {
  const sales = await ctx.prisma.sale.findMany({
    where: { deletedAt: null },
  })

  return salesSelectOutput.parse(sales)
})

saleSelectOutput 说明

  • saleSelectOutput 是基于 saleSchema 修改/省略部分字段得到的Zod校验规则,saleSchema 同时用于创建 createSaleInput
  • salesSelectOutput 是 saleSelectOutput 的数组版本

问题详情

代码运行无其他异常,但 sales.[n].dateOfSale 字段校验失败,错误信息:

"code": "invalid_type",
"expected": "date",
"received": "object",
"path": [
  "dateOfSale"
],
"message": "please input a proper date"

查看 dateOfSale 的类型为 object,值为:

2024-02-28T08:09:50.339Z

仅针对该字段单独转换的方案不可行(如下),因为多处存在类似问题:

return salesSelectOutput.parse({...sales, dateOfSale: new Date(dateOfSale)})

需求

如何让Zod接受Prisma返回的日期对象,或在校验前统一转换这类值?


解决方案

方案1:修改Zod Schema兼容Prisma Date对象

Prisma返回的日期是Date实例,但Zod默认的z.date()可能因实例判断严格报错,可自定义兼容校验器:

import { z } from "zod";

// 自定义兼容Prisma Date的校验规则
const prismaDate = z.custom<Date>(
  (val) => val instanceof Date || (typeof val === 'object' && 'toISOString' in val),
  { message: "please input a proper date" }
);

// 替换原schema中的z.date()为prismaDate
const saleSchema = z.object({
  // 其他字段...
  dateOfSale: prismaDate,
  // 其他字段...
});

// 生成对应的输出校验规则
const saleSelectOutput = saleSchema.omit({ /* 需要省略的字段 */ });
const salesSelectOutput = z.array(saleSelectOutput);

方案2:校验前递归转换所有日期字段

编写工具函数统一处理查询结果中的日期字段,避免逐个修改:

// 递归转换日期字段的工具函数
const transformDates = (data) => {
  if (data instanceof Date) return new Date(data);
  if (Array.isArray(data)) return data.map(transformDates);
  if (typeof data === 'object' && data !== null) {
    return Object.fromEntries(
      Object.entries(data).map(([key, val]) => [key, transformDates(val)])
    );
  }
  return data;
};

// 修改查询逻辑
export const getSales = procedure.query(async ({ ctx }) => {
  const sales = await ctx.prisma.sale.findMany({
    where: { deletedAt: null },
  });

  const transformedSales = transformDates(sales);
  return salesSelectOutput.parse(transformedSales);
});

方案3:让Prisma直接返回ISO字符串

在查询时指定日期字段返回ISO字符串,适配Zod的z.string().datetime()规则:

export const getSales = procedure.query(async ({ ctx }) => {
  const sales = await ctx.prisma.sale.findMany({
    where: { deletedAt: null },
    select: {
      // 保留其他需要的字段
      dateOfSale: { select: { toISOString: true } },
    },
  });

  // 提取ISO字符串
  const processedSales = sales.map(sale => ({
    ...sale,
    dateOfSale: sale.dateOfSale.toISOString,
  }));

  return salesSelectOutput.parse(processedSales);
});

内容的提问来源于stack exchange,提问作者The Blind Hawk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:26:33