示例说明
传统 RPC 的类型契约靠 proto 文件或 OpenAPI 中转,前后端各自维护易漂移。tRPC 直接把服务端路由的 TypeScript 类型导出给客户端:input 的 Zod schema 既是运行时校验器也是类型来源,调用参数不符在客户端编译期就报错,服务端校验兜底防伪造请求,一层定义两层防护。
技术要点
- Zod schema 同时承担校验与类型推断
- router 类型导出驱动客户端智能提示
- query 与 mutation 语义分流
代码解读
三个过程覆盖读查、分页列表与创建:byId 要求 UUID;list 的分页参数带默认值与上下限约束;create 校验标题与正文的长度域。context 泛型让 ctx.db 在过程内获得类型,AppRouter 类型是客户端 createTRPCReact 的输入。
使用建议
公开过程仍要按用户身份过滤数据,校验不等于授权。错误信息默认会跨端传递,注意脱敏。拆分子路由组合成根路由,保持文件可维护。
代码示例
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { createHTTPContext } from './context';
const t = initTRPC.context<typeof createHTTPContext>().create();
const publicProcedure = t.procedure;
const router = t.router;
const postRouter = router({
byId: publicProcedure
.input(z.object({ id: z.string().uuid() }))
.query(async ({ input, ctx }) => {
return ctx.db.posts.find((p) => p.id === input.id) ?? null;
}),
list: publicProcedure
.input(z.object({
page: z.number().int().min(1).default(1),
limit: z.number().int().min(1).max(50).default(20),
}))
.query(async ({ input, ctx }) => {
const start = (input.page - 1) * input.limit;
return ctx.db.posts.slice(start, start + input.limit);
}),
create: publicProcedure
.input(z.object({
title: z.string().min(3).max(120),
body: z.string().min(1),
}))
.mutation(async ({ input, ctx }) => {
const post = { id: crypto.randomUUID(), ...input };
ctx.db.posts.push(post);
return post;
}),
});
export const appRouter = router({ posts: postRouter });
export type AppRouter = typeof appRouter;