输入校验
使用 Zod Schema 校验 Procedure 输入。
oRPC 原生支持 Zod Schema 作为输入校验器。通过 .input() 方法链式调用,你可以在 Procedure 执行前验证输入数据,校验失败时自动返回错误。
基本用法
使用 z.object() 定义输入 Schema,通过 .input() 链接到 Procedure:
import { z } from "zod"
import { protectedProcedure } from "../index"
export const profileRouter = {
update: protectedProcedure
.input(
z.object({
defaultModel: z.string().optional(),
preferredLocale: z.string().optional(),
outputTone: z.string().optional(),
marketingOptIn: z.boolean().optional(),
}),
)
.handler(async ({ context, input }) => {
// input 的类型自动推断为:
// { defaultModel?: string; preferredLocale?: string; outputTone?: string; marketingOptIn?: boolean }
const db = createDb()
const userId = context.session.user.id
const [existing] = await db
.select()
.from(profiles)
.where(eq(profiles.userId, userId))
.limit(1)
if (existing) {
const [updated] = await db
.update(profiles)
.set({ ...input, updatedAt: new Date() })
.where(eq(profiles.userId, userId))
.returning()
return updated
}
const [created] = await db
.insert(profiles)
.values({ userId, ...input })
.returning()
return created
}),
}实际示例
必填参数
// 获取单个文档
get: protectedProcedure
.input(z.object({ id: z.string().min(1) }))
.handler(async ({ context, input }) => {
const [doc] = await db
.select()
.from(documents)
.where(and(eq(documents.id, input.id), eq(documents.userId, context.session.user.id)))
.limit(1)
if (!doc) throw new Error("Document not found")
return doc
}),多个必填参数
// 发送消息
sendMessage: protectedProcedure
.input(z.object({
conversationId: z.string().min(1),
content: z.string().min(1),
}))
.handler(async ({ context, input }) => {
// input.conversationId: string
// input.content: string
const [userMsg] = await db
.insert(messages)
.values({
conversationId: input.conversationId,
userId: context.session.user.id,
role: "user",
content: input.content,
})
.returning()
return userMsg
}),可选参数与默认值
// 创建对话
create: protectedProcedure
.input(z.object({
title: z.string().min(1),
task: z.string().optional(),
}))
.handler(async ({ context, input }) => {
const [convo] = await db
.insert(conversations)
.values({
userId: context.session.user.id,
title: input.title,
task: input.task ?? "summarize",
})
.returning()
return convo
}),错误处理
当输入校验失败时,oRPC 会自动返回校验错误。你也可以在 handler 中手动抛出错误:
ORPCError
oRPC 提供了 ORPCError 类用于抛出标准化的错误:
import { ORPCError } from "@orpc/server"
export const documentRouter = {
get: protectedProcedure
.input(z.object({ id: z.string().min(1) }))
.handler(async ({ context, input }) => {
const [doc] = await db
.select()
.from(documents)
.where(and(eq(documents.id, input.id), eq(documents.userId, context.session.user.id)))
.limit(1)
if (!doc) {
throw new ORPCError("NOT_FOUND", { message: "Document not found" })
}
return doc
}),
}全局错误拦截器
在 RPC 处理器级别配置全局错误拦截器,所有未捕获的错误都会被记录:
// src/routes/api/rpc/$.ts
const rpcHandler = new RPCHandler(appRouter, {
interceptors: [
onError((error) => {
logger.error("rpc.error", { feature: "rpc", error: serializeError(error) })
}),
],
})类型推断
oRPC 的核心优势之一是类型自动传递。服务端定义的 Zod Schema 会自动推断出 TypeScript 类型,并通过 @orpc/server 的 RouterClient 类型传递到客户端。
// 服务端
export type AppRouter = typeof appRouter
export type AppRouterClient = RouterClient<typeof appRouter>// 客户端——orpc.todos.create 的 input 类型自动推断
const mutation = orpc.todos.create.useMutation({
// TypeScript 知道 input 需要 { title: string; task?: string }
})你不需要在客户端重复定义接口类型或 Zod Schema。类型从服务端到客户端完全自动推导,实现了真正的端到端类型安全。