tRPC 实战:从 REST 到端到端类型安全 API
tRPC 让前后端共享 TypeScript 类型:服务端定义 procedure,客户端自动获得完整类型推导。本文从 Express API 改造实战 tRPC。
tRPC 的核心魔法:服务端用 router 定义 procedure,客户端导入 router 类型即获得全链路类型推导——改服务端返回值,前端编译报错。无需 Schema、无需代码生成,全栈 TS 项目的 API 最优解。
为什么是 tRPC
REST 的类型断裂:后端改了响应结构,前端运行时才崩。GraphQL 解决它靠 Schema + 代码生成(重);tRPC 的答案:直接共享 TypeScript 类型——零样板、零生成步骤。
从 Express 到 tRPC
// server/trpc.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const router = t.router;
export const publicProcedure = t.procedure;
// server/routers/user.ts
export const userRouter = router({
list: publicProcedure.query(() => db.users.findAll()),
byId: publicProcedure
.input(z.string()) // zod 校验输入
.query(({ input }) => db.users.findById(input)),
create: publicProcedure
.input(z.object({ name: z.string(), email: z.string().email() }))
.mutation(({ input }) => db.users.create(input)),
});
// server/index.ts
export const appRouter = router({ user: userRouter });
export type AppRouter = typeof appRouter; // 类型导出是全链路的钥匙
// client:导入类型(只导入 type,零运行时代码)
import type { AppRouter } from '../server';
const trpc = createTRPCClient<AppRouter>({ links: [httpBatchLink({ url: '/trpc' })] });
const user = await trpc.user.byId.query('123');
// ^? 完整类型推导:参数类型、返回值类型、自动补全
模块化结构
路由按领域拆分(userRouter/postRouter),mergeRouters 或嵌套组合。中间件机制(t.middleware)实现认证:
const protectedProcedure = publicProcedure.use(async ({ ctx, next }) => {
if (!ctx.user) throw new TRPCError({ code: 'UNAUTHORIZED' });
return next({ ctx: { user: ctx.user } });
});
React 集成
@trpc/react-query 把 procedure 包装成 TanStack Query hooks——trpc.user.list.useQuery() 直接获得缓存/加载态/类型三全。
边界
- 仅适合前后端同 TS 仓库(monorepo 最理想);
- 对外公开 API 仍需 REST/GraphQL——tRPC 是"内部 API"的王者;
- 与 Next.js App Router / Hono 的 RPC 模式存在竞争——理念相同。
验证接口上线后的延迟与错误
tRPC 实战完成开发验证后,可选一条包含数据库或外部调用的业务路径接入观测云。按 Node.js APM 文档确认探针初始化顺序与框架兼容性,检查请求及下游 Span 是否连续,再按文档注入日志关联字段。以路由模板或稳定的操作名聚合,避免用用户 ID、动态 URL 或完整 GraphQL 查询作为高基数标签。
接入文档:Node.js APM 接入 · 日志采集 · 日志与链路关联。
常见问题(FAQ)
Q:tRPC 和 GraphQL 怎么选?
全栈 TS 内部通信:tRPC(零样板);跨语言、对外 API、复杂查询图:GraphQL。
Q:输入校验用什么?
Zod 是标准搭配(procedure 的 input 校验器),Valibot/TypeBox 也可——校验结果同时成为输入类型。
Q:错误怎么处理?
TRPCError 带 code(NOT_FOUND/FORBIDDEN...)抛出,客户端按 code 分支——错误也有类型。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。