Remix项目使用Prisma Zod Generator遇ZodUnion解析错误
问题背景
在Remix项目中集成Prisma Zod Generator工具时,更新模型遇到解析错误。定义的Prisma模型如下:
model TextContent { content Content @relation(fields: [contentId], references: [id], onDelete: Cascade) contentId String @id text String }
使用生成的TextContentUncheckedUpdateInputObjectSchema配合remix-params-helper的getFormDataOrFail方法解析FormData时,触发以下错误:
Error: Unexpected type ZodUnion for key contentId at processDef (my-app/node_modules/remix-params-helper/dist/cjs/helper.js:179:15) at processDef (my-app/node_modules/remix-params-helper/dist/cjs/helper.js:162:9) at parseParams (my-app/node_modules/remix-params-helper/dist/cjs/helper.js:36:9) at getParamsInternal (my-app/node_modules/remix-params-helper/dist/cjs/helper.js:54:9) at getFormDataOrFail (my-app/node_modules/remix-params-helper/dist/cjs/helper.js:120:20) at processTicksAndRejections (node:internal/process/task_queues:96:5) at action (my-app/app/routes/contents.$contentId.tsx:12:15) at Object.callRouteActionRR (my-app/build/server.js:39649:20) at callLoaderOrAction (my-app/build/server.js:38772:20) at submit (my-app/build/server.js:38365:20)
已确认FormData中的contentId和text均为字符串值,且contentId同时通过路由params和FormData传递。
原因分析
Prisma Zod Generator生成的UncheckedUpdateInput类型对应的Zod Schema中,contentId字段是字符串与关联对象的联合类型(ZodUnion)——这是因为UncheckedUpdateInput允许两种更新方式:直接传入外键ID字符串,或者传入关联的Content对象进行嵌套更新。但remix-params-helper工具目前不支持解析ZodUnion类型,因此触发了错误。
此外,你的更新场景中contentId已经通过路由params获取,完全不需要从FormData中传递该字段,这也是导致多余解析错误的原因之一。
解决方案
方案1:裁剪生成的Schema,保留必要字段
使用Zod的pick方法,只提取需要验证的text字段,避免解析不需要的contentId字段:
export async function action({ request, params }: ActionArgs) { // 仅保留text字段用于验证 const schema = TextContentUncheckedUpdateInputObjectSchema.pick({ text: true }); const data = await getFormDataOrFail(request, schema); await prisma.textContent.update({ data, where: { contentId: params.contentId } }); return null; }
方案2:自定义Zod Schema替代生成的类型
如果需要更灵活的验证规则,直接编写自定义Zod Schema,绕开生成的Union类型问题:
import { z } from "zod"; // 自定义更新验证规则 const UpdateTextContentSchema = z.object({ text: z.string(), }); export async function action({ request, params }: ActionArgs) { const data = await getFormDataOrFail(request, UpdateTextContentSchema); await prisma.textContent.update({ data, where: { contentId: params.contentId } }); return null; }
方案3:强制覆盖contentId的Schema类型(若需传递该字段)
如果业务场景确实需要从FormData中传递contentId,可以手动覆盖该字段的Schema类型,将其强制指定为字符串:
export async function action({ request, params }: ActionArgs) { const schema = TextContentUncheckedUpdateInputObjectSchema.extend({ contentId: z.string(), }); const data = await getFormDataOrFail(request, schema); await prisma.textContent.update({ data, where: { contentId: data.contentId // 或使用params.contentId,根据业务场景选择 } }); return null; }
内容的提问来源于stack exchange,提问作者taxfree

