佐迪克斯语Name
佐迪克斯是一个集合 佐德语Name 水电费 反应路由器 v7 装载器和动作。 它总结了解析和验证的复杂性 FormData 财务报告和审定财务报表 URLSearchParams 所以,你的装载器/动作保持干净 并强烈地打字.
现在全力支持Zod v3和v4!
有反应的路由器加载器往往看起来像:
export async function loader({ params, request }: Route.LoaderArgs) {
const { id } = params
const url = new URL(request.url)
const count = url.searchParams.get('count') || '10'
if (typeof id !== 'string') {
throw new Error('id must be a string')
}
const countNumber = parseInt(count, 10)
if (isNaN(countNumber)) {
throw new Error('count must be a number')
}
// Fetch data with id and countNumber
}
这是和Zodix相同的装载器:
export async function loader({ params, request }: Route.LoaderArgs) {
const { id } = zx.parseParams(params, { id: z.string() })
const { count } = zx.parseQuery(request, { count: zx.NumAsString })
// Fetch data with id and count
}
检查 示例应用程序 用于完整的常见模式实例.
要点
- 完全 Zod v3 和 v4 兼容性 - 与两个版本无缝工作
- 动态计划支持 - 根据数据库/API数据在运行时建立计划
- 大幅降低回放路由器动作/装入器 bloat
- 避免 FormData 和 URLSearchParams 的异常
- 无外部依赖的微小( N)不足 1kb 克齐克(中文(简体) )
- 使用现有的 Zod 策略, 或者在苍蝇上写入
- 自定义字符串数字、布尔和复选框的 Zod 方案
- 默认情况下丢出用于 React 路由器错误边界的错误
- 支持对自定义验证/错误进行非抛出解析
- 与所有 React 路由器运行时间( 节点、 Deno、 Vercel、 Cloudflare等) 一起工作
- 满 单位测试覆盖率
设置
安装 npm, 纱线, pnpm等 .
npm install @coji/zodix zod
Zod 版本兼容性
Zodix通过单独的导入路径支持Zod v3和v4:
- Zod v3 (英语): 使用
@coji/zodix(所需经费)zod@^3.25.0(或后来) - 佐德 v4 (英语): 使用
@coji/zodix/v4(所需经费)zod@^4.0.0(中文(简体) )
// For Zod v3
import { zx } from '@coji/zodix'
// For Zod v4
import { zx } from '@coji/zodix/v4'
移徙指南
从 Zod v3 升级到 v4 ? 遵循这些步骤:
- 更新您的 Zod 依赖 :
npm install zod@^4.0.0 - 必要时审查并迁移您的 Zod 计划 - 看 Zod v4 更改日志 用于断开更改
- 更改您的 Zodix 从
@coji/zodix改为@coji/zodix/v4 - 就这样
使用量
导入 zx 对象,或特定函数:
import { zx } from '@coji/zodix'
// import { parseParams, NumAsString } from '@coji/zodix';
zx.parse-Params(参数:params,计划:Schema)
分析并验证 Params 对象从 Route.LoaderArgs['params'] 或者说 Route.ActionArgs['params'] 使用 Zod 形状:
export async function loader({ params }: Route.LoaderArgs) {
const { userId, noteId } = zx.parseParams(params, {
userId: z.string(),
noteId: z.string(),
})
}
与以上相同,但使用已存在的 Zod 对象方案:
// This is if you have many pages that share the same params.
export const ParamsSchema = z.object({ userId: z.string(), noteId: z.string() })
export async function loader({ params }: Route.LoaderArgs) {
const { userId, noteId } = zx.parseParams(params, ParamsSchema)
}
zx.parseForm( 请求: 请求, 计划: Schema)
解析和验证 FormData 从a Request 在回放路由器动作中避免乏味 FormData 舞会:
export async function action({ request }: Route.ActionArgs) {
const { email, password, saveSession } = await zx.parseForm(request, {
email: z.string().email(),
password: z.string().min(6),
saveSession: zx.CheckboxAsString,
})
}
与现有的Zod计划和模型/控制器相结合:
// db.ts
export const CreateNoteSchema = z.object({
userId: z.string(),
title: z.string(),
category: NoteCategorySchema.optional(),
})
export function createNote(note: z.infer<typeof CreateNoteSchema>) {}
import { CreateNoteSchema, createNote } from './db'
export async function action({ request }: Route.ActionArgs) {
const formData = await zx.parseForm(request, CreateNoteSchema)
createNote(formData) // No TypeScript errors here
}
zx.parse查询(请求:请求,计划:Schema)
分析并验证 a 的查询字符串( 搜索参数) Request数字 :
export async function loader({ request }: Route.LoaderArgs) {
const { count, page } = zx.parseQuery(request, {
// NumAsString parses a string number ("5") and returns a number (5)
count: zx.NumAsString,
page: zx.NumAsString,
})
}
zx.parseParamsSafe () / zx.parseFormSafe () / zx.parseQuerySafe ()
这些功能与非安全版本相同,但当验证失败时不要抛出. 他们用 z.parseSafe() ,并总是返回带有解析数据或错误的对象.
export async function action(args: Route.ActionArgs) {
const results = await zx.parseFormSafe(args.request, {
email: z.string().email({ message: 'Invalid email' }),
password: z
.string()
.min(8, { message: 'Password must be at least 8 characters' }),
})
return {
success: results.success,
error: results.error,
}
}
检查 登录页面示例 一个完整的例子.
处理错误
parseParams(), (中文(简体) ), parseForm(),以及 parseQuery()
当解析失败时, 这些函数会丢出一个 400 个响应 。 这件不错 反应路由器出错边界 并应该用于解析很少会失败并且不需要自定义错误处理的东西. 您可以通过自定义错误消息或状态代码.
export async function loader({ params }: Route.LoaderArgs) {
const { postId } = zx.parseParams(
params,
{ postId: zx.NumAsString },
{ message: "Invalid postId parameter", status: 400 }
);
const post = await getPost(postId);
return { post };
}
export function ErrorBoundary() {
const error = useRouteError();
return <h1>Error: {error.statusText}</h1>;
}
检查 页面示例 一个完整的例子.
parseParamsSafe(), (中文(简体) ), parseFormSafe(),以及 parseQuerySafe()
这些功能对于形式验证是巨大的,因为它们在解析失败时不会抛出. 它们总是以这样的形状返回一个物体:
{ success: boolean; error?: ZodError; data?: <parsed data>; }
然后可以处理动作中的错误,并在组件中使用 useActionData()。检查 登录页面示例 一个完整的例子.
动态图
有时需要根据运行时间数据(如从数据库中过滤选项)动态地构建计划. Zodix为这种模式提供了出色的支持.
类型推论帮助器
使用 InferParams 从您的策略中获取明确类型的类型:
import { zx, type InferParams } from '@coji/zodix'
const mySchema = z.object({
q: z.string().optional(),
page: zx.IntAsString.optional(),
})
type Params = InferParams<typeof mySchema>
// → { q: string | undefined, page: number | undefined }
以 z.obobject (. extend () 构建动态形状
建议采用的动态计划办法 z.object().extend()数字 :
export async function loader({ request }: Route.LoaderArgs) {
// Fetch dynamic filter categories from database/API
const filterCategories = await getAvailableFilterCategories()
// → [{ id: 'category', ... }, { id: 'brand', ... }, ...]
// 1. Define base schema with known fields
const baseSchema = z.object({
q: z.string().optional(),
page: zx.IntAsString.optional(),
})
// 2. Build dynamic fields from runtime data
const dynamicFields = Object.fromEntries(
filterCategories.map((cat) => [cat.id, z.string().optional()]),
)
// 3. Extend base schema with dynamic fields
const fullSchema = baseSchema.extend(dynamicFields)
// 4. Parse with full type inference (Zod v4)
const parsed = zx.parseQuery(request, fullSchema)
// ✨ parsed.q is string | undefined
// ✨ parsed.page is number | undefined
// ✨ parsed[category.id] is string | undefined
}
说明: 使用 Zod v3, 动态字段的类型推论可能返回 any 由于TypeScript的限制. 为了与v3进行更好的类型安全,考虑其他方法.
备选办法
关于更先进的使用案例和替代模式(URLSearchParams、JSON记录等),见 动态 Schemas 指南本指南涵盖:
- 三种不同的取舍办法
- Zod v3 vs v4 考虑因素
- 实际世界实例(电子商务过滤器、管理仪表板、多租户SaaS)
- 集中式计划定义的最佳做法
检查 动态过滤器示例 (c) 全面开展工作.
援助者 Zod Schemas
因为 FormData 财务报告和审定财务报表 URLSearchParams 将所有值序列化为字符串,你往往会遇到诸如 "5", (中文(简体) ), "on" 财务报告和审定财务报表 "true"。帮助者 schemas 处理解析和验证代表其他数据类型的字符串,并打算与解析函数一起使用.
可用的助手
zx. BoolAsstring (英语)
"true"→true"false"→false"notboolean"掷出ZodError
zx. 检查框
"on"→trueundefined→false"anythingbuton"掷出ZodError
z. 插入结构
"3"→3"3.14"掷出ZodError"notanumber"掷出ZodError
z. 数字结构
"3"→3"3.14"→3.14"notanumber"掷出ZodError
见 测试 更多细节.
使用量
const Schema = z.object({
isAdmin: zx.BoolAsString,
agreedToTerms: zx.CheckboxAsString,
age: zx.IntAsString,
cost: zx.NumAsString,
})
const parsed = Schema.parse({
isAdmin: 'true',
agreedToTerms: 'on',
age: '38',
cost: '10.99',
})
/*
parsed = {
isAdmin: true,
agreedToTerms: true,
age: 38,
cost: 10.99
}
*/
Zod v3/v4 兼容性细节
如何运作
Zodix为 Zod v3 和 v4 兼容性提供了单独的导入路径 :
- 使用适当的导入路径 基于您的 Zod 版本
- 全类型安全 两个版本都保留
- 相同的 API 跨越两个版本 - 只有导入路径更改
与 Zod v3 使用
import { z } from 'zod' // v3.x
import { zx } from '@coji/zodix' // Default path for v3
const schema = z.object({
name: z.string(),
age: zx.IntAsString,
})
export async function loader({ params }: Route.LoaderArgs) {
const data = zx.parseParams(params, schema) // Works with Zod v3!
}
与 Zod v4 使用
import { z } from 'zod' // v4.x
import { zx } from '@coji/zodix/v4' // Use v4 path
const schema = z.object({
name: z.string(),
age: zx.IntAsString,
})
export async function loader({ params }: Route.LoaderArgs) {
const data = zx.parseParams(params, schema) // Works with Zod v4!
}
额外
自定义 URLSearchParams 解析
您可能有包含查询字符串的 URL, 看上去像 ?ids[]=1&ids[]=2 或者说 ?ids=1,2 里面的建筑没有像现在这样 URLSearchParams 解析.
您可以通过自定义函数, 或者使用库 查询字符串 用Zodix来分析它们.
// Create a custom parser function
type ParserFunction = (
params: URLSearchParams,
) => Record<string, string | string[]>
const customParser: ParserFunction = () => {
/* ... */
}
// Parse non-standard search params
const search = new URLSearchParams(`?ids[]=id1&ids[]=id2`)
const { ids } = zx.parseQuery(
request,
{ ids: z.array(z.string()) },
{ parser: customParser },
)
// ids = ['id1', 'id2']
有多重意图的行动
Zod受歧视的工会在帮助处理这种多重意图的行动方面表现得很好:
// This adds type narrowing by the intent property
const Schema = z.discriminatedUnion('intent', [
z.object({ intent: z.literal('delete'), id: z.string() }),
z.object({ intent: z.literal('create'), name: z.string() }),
])
export async function action({ request }: Route.ActionArgs) {
const data = await zx.parseForm(request, Schema)
switch (data.intent) {
case 'delete':
// data is now narrowed to { intent: 'delete', id: string }
return { success: true }
case 'create':
// data is now narrowed to { intent: 'create', name: string }
return { success: true }
default:
// data is now narrowed to never. This will error if a case is missing.
const _exhaustiveCheck: never = data
}
}
承认
这个项目是一个叉子 里托马塞克/佐迪克斯感谢Riley Tomasek创建了最初的Zodix图书馆.