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

如何在Prisma中为JSON字段处理Date类型的类型定义?

Prisma + PlanetScale(MySQL)JSON字段处理Date类型的解决方案

问题根源

Prisma的InputJsonValue类型不兼容原生Date对象,同时MySQL的JSON类型仅支持字符串、数字、布尔、数组、对象、null这些基础类型,Date对象无法直接被序列化存储,所以会触发TS类型错误;直接用JSON.stringify()会导致Prisma二次序列化,产生双重转义的JSON字符串。


解决方案1:手动转换Date字段(简单直接)

存入数据库前将Date转为ISO字符串,取出时再转回Date对象,避免类型错误和双重转义。

// 存入前处理Date字段
const processedTp = {
  ...tp,
  trained_at: tp.trained_at.toISOString()
};

const newPrompt = await prisma.tunePrompt.create({
  data: { apiData: processedTp }
});

// 取出后还原Date字段
const savedPrompt = await prisma.tunePrompt.findUnique({ where: { id: newPrompt.id } });
if (savedPrompt?.apiData?.trained_at) {
  savedPrompt.apiData.trained_at = new Date(savedPrompt.apiData.trained_at);
}
  • 优点:无额外依赖,实现简单
  • 缺点:读写操作需手动处理,存在代码重复

解决方案2:Prisma中间件自动处理

通过Prisma中间件全局拦截读写操作,自动完成Date的序列化/反序列化,无需重复编写转换逻辑。

// 序列化:Date转ISO字符串
function serializeDates(obj: any): any {
  if (obj instanceof Date) {
    return obj.toISOString();
  } else if (Array.isArray(obj)) {
    return obj.map(item => serializeDates(item));
  } else if (typeof obj === 'object' && obj !== null) {
    return Object.fromEntries(
      Object.entries(obj).map(([key, value]) => [key, serializeDates(value)])
    );
  }
  return obj;
}

// 反序列化:ISO字符串转Date
function deserializeDates(obj: any): any {
  if (typeof obj === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(obj)) {
    return new Date(obj);
  } else if (Array.isArray(obj)) {
    return obj.map(item => deserializeDates(item));
  } else if (typeof obj === 'object' && obj !== null) {
    return Object.fromEntries(
      Object.entries(obj).map(([key, value]) => [key, deserializeDates(value)])
    );
  }
  return obj;
}

// 注册Prisma中间件
prisma.$use(async (params, next) => {
  // 处理写入操作(create/update/upsert等)
  if (['create', 'update', 'upsert'].includes(params.action)) {
    if (params.args.data.apiData) {
      params.args.data.apiData = serializeDates(params.args.data.apiData);
    }
  }

  // 处理读取操作(find/findMany等)
  const result = await next(params);
  if (result && typeof result === 'object') {
    if (Array.isArray(result)) {
      return result.map(item => item.apiData ? { ...item, apiData: deserializeDates(item.apiData) } : item);
    } else {
      return result.apiData ? { ...result, apiData: deserializeDates(result.apiData) } : result;
    }
  }
  return result;
});
  • 优点:全局自动处理,无重复代码
  • 缺点:需维护中间件逻辑,复杂嵌套结构可能需要调整日期匹配正则

解决方案3:扩展InputJsonValue类型(仅解决TS报错,需配合运行时处理)

如果只是想消除TS编译错误,可以扩展Prisma的InputJsonValue类型,让它支持Date,但运行时仍需将Date转为字符串(否则MySQL无法存储)。

在项目根目录创建prisma/types.d.ts文件:

import { InputJsonValue as OriginalInputJsonValue } from '@prisma/client';

type InputJsonValue = OriginalInputJsonValue | Date;

declare module '@prisma/client' {
  interface InputJsonValue extends OriginalInputJsonValue {
    [key: string]: InputJsonValue | Date | string | number | boolean | null;
  }
}

注意:此方案仅解决类型提示问题,必须配合方案1或2的运行时转换,否则写入数据库时会失败。


为什么JSON.stringify会导致双重转义?

Prisma会自动将传入的JSON对象序列化为JSON字符串存入MySQL,若提前调用JSON.stringify(tp),Prisma会再次对这个字符串进行序列化,最终存储的是嵌套的JSON字符串(例如"{\"trained_at\":\"2024-01-01T00:00:00.000Z\"}"),而非预期的JSON对象。

内容的提问来源于stack exchange,提问作者dcsan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:00:56