如何将Supabase自动生成的TS类型与Zod同步以验证API数据?
解决方案:从Supabase自动生成的TypeScript类型生成Zod Schema
针对你的需求,以下是两种实用方案,既可以自动同步Supabase数据库结构,又支持添加自定义验证规则:
方案一:使用专用工具自动生成(推荐)
这类工具专门适配Supabase生成的Database类型结构,能一键生成基础Zod Schema,同时允许你通过配置注入自定义验证逻辑。
1. 使用supabase-zod
- 安装依赖:
npm install supabase-zod --save-dev - 创建配置文件(项目根目录下
supabase-zod.config.ts):import { defineConfig } from "supabase-zod"; export default defineConfig({ supabaseTypesPath: "./types/supabase.ts", // 你的Supabase类型文件路径 outputPath: "./src/zod/schemas.ts", // 生成的Zod Schema输出路径 // 自定义验证规则配置 customValidations: { public: { ingredients: { Insert: { name: (z) => z.string().min(1, "Ingredient name is required"), calories: (z) => z.number().nullable().optional().refine(val => val === null || val >= 0, "Calories must be non-negative") } } } } }); - 添加执行脚本到
package.json:{ "scripts": { "generate-zod": "supabase-zod generate", "update-supabase": "supabase gen types typescript --linked > types/supabase.ts && npm run generate-zod" } } - 使用方式:每次更新Supabase数据库结构后,执行
npm run update-supabase,即可自动同步生成带自定义规则的Zod Schema。
2. 使用zod-db
这是一款通用型数据库类型转Zod工具,同样支持Supabase类型:
- 安装依赖:
npm install zod-db --save-dev - 编写生成脚本(比如
scripts/generate-zod.ts):import { generateZodSchemas } from "zod-db"; import fs from "fs"; import type { Database } from "../types/supabase"; const schemas = generateZodSchemas<Database>({ tables: { "public.ingredients": { insert: { name: (schema) => schema.min(1, "Ingredient name is required") } } } }); // 将生成的Schema写入文件 const output = `import { z } from "zod";\n\nexport const ingredientSchema = ${schemas.public.ingredients.Insert.toString()};`; fs.writeFileSync("./src/zod/ingredient.ts", output); - 添加执行脚本到
package.json,方便一键更新。
方案二:手动实现类型推导(适合完全自定义场景)
如果不想依赖第三方工具,可以手动基于TypeScript类型映射生成基础Schema,再叠加自定义规则:
步骤1:编写基础Schema生成函数
import { z } from "zod"; import type { Database } from "./types/supabase"; // 定义数据库类型到Zod类型的映射逻辑 type ZodFromDbType<T> = T extends string ? z.ZodString : T extends number ? z.ZodNumber : T extends boolean ? z.ZodBoolean : T extends null ? z.ZodNullable<ZodFromDbType<Exclude<T, null>>> : z.ZodUnknown; // 生成指定表的基础Insert Schema function generateBaseInsertSchema<T extends Record<string, any>>(table: { Insert: T }) { const schemaEntries: [string, z.ZodTypeAny][] = []; for (const key in table.Insert) { type FieldType = T[typeof key]; let zodType: ZodFromDbType<FieldType>; // 匹配基础类型 if (typeof (table.Insert as any)[key] === "string") { zodType = z.string() as ZodFromDbType<FieldType>; } else if (typeof (table.Insert as any)[key] === "number") { zodType = z.number() as ZodFromDbType<FieldType>; } else { zodType = z.unknown() as ZodFromDbType<FieldType>; } // 处理可选字段 if (key in table.Insert && (table.Insert as any)[key] === undefined) { zodType = zodType.optional() as ZodFromDbType<FieldType>; } // 处理可空字段 if (null extends FieldType) { zodType = zodType.nullable() as ZodFromDbType<FieldType>; } schemaEntries.push([key, zodType]); } return z.object(Object.fromEntries(schemaEntries)); }
步骤2:生成Schema并添加自定义规则
// 生成ingredients表的基础Insert Schema const baseIngredientSchema = generateBaseInsertSchema(Database.public.Tables.ingredients); // 叠加自定义验证规则 export const ingredientSchema = baseIngredientSchema.extend({ name: (schema) => schema.min(1, "Ingredient name is required"), calories: (schema) => schema.refine(val => val === null || val >= 0, "Calories cannot be negative") });
保持同步的最佳实践
- 将Supabase类型更新和Zod Schema生成绑定为一个脚本(如方案一中的
update-supabase),避免手动操作遗漏。 - 使用Git钩子(如pre-commit),在提交代码前自动检查并更新Zod Schema,确保代码库中类型和Schema始终同步。
内容的提问来源于stack exchange,提问作者user12291772
相关产品推荐
相关产品推荐

