改后端一个字段名,前端炸了一片,你得逐个文件去查是哪个接口没同步——这种事你遇到过吧?更离谱的是,API 文档写得比代码还长,结果上线第一天文档就过时了。
这还真不怪你。是 REST 和 GraphQL 那套「先写 Schema、再生成类型、再手动同步」的玩法,在 2026 年已经有点跟不上了。
tRPC 给了完全不同的解法:你的 TypeScript 类型就是 API 契约。没有中间商,不用代码生成器。后端改了,前端编译时就报错——不是在线上才发现,IDE 里它就告诉你了。

全栈 TypeScript 的最后一公里
2026 年你会发现一个现象:新项目基本都在用元框架。Next.js、Nuxt、SvelteKit、SolidStart……这些框架的共性是——它们就是全栈的。
全栈意味着什么?前端和后端写在一个项目里,同一个 repo,有时候甚至同一个文件夹。服务器组件直接 fetch 数据库,Server Actions 直接处理表单提交。前端不再只是「发 GET/POST 请求的客户端」,它和后端绑得越来越紧。
但你的 API 层还是 REST 那一套。
– 在 `pages/api` 下写个 handler
– 手动定义请求体类型
– 手动写响应类型
– 前端再手动定义一份一样的
– 跑一遍 build,发现类型对不上
「定义两份、手动同步」的模式,既累又不安全。以前前后端分开部署还能忍,现在代码都混在一个项目里了,还搞两套类型真心说不过去。
tRPC全栈安全框架要解决的就是这个痛点——只用 TypeScript 定义一次,前端直接消费,类型自动贯穿整个链路。
怎么工作的?
前端像调本地函数一样调后端 API,所有参数和返回值类型都在编译时搞定。
看个最简单的例子。定义后端路由:
```typescript
// server/router.ts
export const appRouter = t.router({
getUserById: t.procedure
.input(z.string())
.query(({ input }) => {
return db.user.findUnique({ where: { id: input } });
}),
});
```
客户端直接用:
```typescript
// client/UserProfile.tsx
const user = trpc.getUserById.useQuery("user_123");
// user.data 的类型就是 Prisma User 类型
// 啥都不用写
```
你注意到没?没有 RequestBody 类型、没有 Response 类型、没有 URL 路径、没有 HTTP 方法——前后端之间的类型就这么静悄悄地同步了。
如果手滑把后端 `getUserById` 的返回改成了 `{ id: string, name: string }`,前端用到不存在的字段的地方,编译时直接飘红。比运行时靠单元测试去抓爽多了。
tRPC v11 有什么新东西
2026 年 5 月,tRPC v11 已经是稳定版,新项目基本都把它当标配了。如果你还在用 v10,下面这几个值得一看:
TanStack Query v5 深度集成。React Suspense 全面支持、数据预取优化、缓存策略改进。以前用 tRPC 还要手动配一堆 query 选项,现在开箱即用。
非 JSON 内容类型支持。v11 终于支持 `FormData`、`Blob`、`File`、`Uint8Array` 了。处理图片上传不用再单独走 REST 端点,全在 tRPC 搞定。
SSE 订阅。tRPC v11 全面支持 Server-Sent Events 做实时订阅,性能比 WebSocket 好,成本更低。配合 React 的 `useSubscription` hook,做实时通知、聊天消息、数据看板都挺方便。
HTTP/2 支持。高吞吐场景下 HTTP/2 的多路复用能减少连接开销。v11 原生支持,性能上了台阶。
路由懒加载。大项目可以按需加载路由,减小初始包体积。和 Next.js App Router 的 `dynamic import`配合效果更好。
和 REST、GraphQL 比,好在哪?
说真的,这三者的取舍其实挺清楚。
REST:通用性强,谁都能用。但得手动定义类型,前后端各维护一套。小项目没问题,项目大了接口文档跟不上代码是常事。缓存好,但拼嵌套资源得多发几次请求。
GraphQL:官方声明式查询语言,想要啥数据自己挑。缺点是新引入一套语言(SDL)、新的运行时、新的客户端。TypeScript 类型要从 GraphQL Schema 生成,多了一层维护。小团队上 GraphQL 负担其实不轻。
tRPC:你的 TypeScript 就是 Schema。没有新的 DSL,没有代码生成,没有多余的抽象层。代价是什么呢?只在 TypeScript 全栈项目里能用。有非 TS 的客户端(比如 iOS/Android 原生)?那就用不了。
简单总结:
– 内部全栈 TypeScript 项目 → tRPC(最省事)
– 要对外暴露 API → REST(最通用)
– 客户端需求复杂多变 → GraphQL(按需查询)
动手:在 Next.js 项目里搭 tRPC
说再多不如跑个代码。下面是一套完整的 tRPC + Next.js App Router 集成方案。
初始化
```bash
npx create-next-app@latest my-app --typescript
cd my-app
pnpm add @trpc/server@11 @trpc/client@11 @trpc/react-query@11 @trpc/next@11
pnpm add @tanstack/react-query@5 zod
```
定义路由
```typescript
// server/index.ts
import { initTRPC } from "@trpc/server";
import { z } from "zod";
const t = initTRPC.create();
export const appRouter = t.router({
greet: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => {
return { message: `你好, ${input.name}!` };
}),
createUser: t.procedure
.input(z.object({
name: z.string().min(1),
email: z.string().email(),
}))
.mutation(({ input }) => {
return { id: "new-uuid", ...input };
}),
});
export type AppRouter = typeof appRouter;
```
设置 API 路由
```typescript
// app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@/server";
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => ({}),
});
export { handler as GET, handler as POST };
```
客户端配置
```typescript
// utils/api.ts
import { createTRPCReact } from "@trpc/react-query";
import type { AppRouter } from "@/server";
export const trpc = createTRPCReact();
```
Provider 包装
```tsx
// app/TRPCProvider.tsx
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import { useState } from "react";
import { trpc } from "@/utils/api";
export function TRPCProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [httpBatchLink({ url: "/api/trpc" })],
})
);
return (
{children}
);
}
```
在组件里用
```tsx
// app/page.tsx
"use client";
import { trpc } from "@/utils/api";
export default function Home() {
const greeting = trpc.greet.useQuery({ name: "testA" });
if (!greeting.data) return <div>加载中...</div>;
return <h1>{greeting.data.message}</h1>;
}
```
搞定。你看完全不用写 `GET /api/trpc/greet?name=xxx` 这种 URL 路径,不用手写 `fetch`,不用处理 JSON 序列化反序列化——类型直接在编译时就焊好了。

认证和中间件:生产级例子
实际项目总离不开认证。tRPC 的中间件干这种活很顺手。
```typescript
// server/index.ts
const isAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({ ctx: { user: ctx.user } });
});
export const protectedProcedure = t.procedure.use(isAuthed);
export const appRouter = t.router({
getProfile: protectedProcedure.query(({ ctx }) => {
return db.user.findUnique({ where: { id: ctx.user.id } });
}),
});
```
受保护的路由不用在每个函数里重复「没认证就抛错」的逻辑——`isAuthed` 中间件处理好了,后面的 procedure 只关心业务。
同样的,日志、限流、数据打点这些横切关注点都可以通过中间件插进去。
2026 年 tRPC 的生态
去 GitHub 看一眼:`trpc/trpc` 38K+ stars,每周 npm 下载量 300 万+。T3 Stack(Next.js + tRPC + Prisma + Auth.js)已经是全栈 TypeScript 的事实标准之一了。
2026 年的 tRPC 生态已经长这样了:
– 认证:Auth.js / Clerk / Lucia Auth 都原生支持 tRPC context
– ORM:Prisma / Drizzle / Kysely 的类型可以直接透传到前端
– 后端框架:Next.js / Nuxt / SvelteKit / SolidStart / Hono 都有官方 adapter
– 部署:Vercel / Cloudflare Workers / Deno Deploy 都跑得很稳
– 文件上传:Uppy / Dropzone + tRPC v11 的 FormData 支持
一个典型的 2026 年全栈项目选型大概是:Next.js 16 + tRPC v11 + Prisma + Auth.js + Drizzle + TanStack Query v5。这一套从数据库到客户端都是端到端类型安全,改字段名不会到处崩。
什么时候不该用?
说实话,tRPC 有自己的局限性。不提前说清楚不太厚道。
– 你不是全 TypeScript 项目。客户端是 iOS/Android 原生,或者团队有 Python/Go 后端,tRPC 只能管 TS 那一层。对外暴露 API 还是得 REST/GraphQL。
– 你需要开放公共 API。tRPC 没有 REST 那样的缓存层,不适合对外。内部通话用 tRPC,对外暴露用 REST——不少团队就这么搭的。
– 团队对 TypeScript 不熟。tRPC 的好处建立在 TS 编译时类型推断上。要是团队还在「any 大法好」的阶段,用起来可能觉得束手束脚。
– 高性能微服务通信。gRPC 在大数据量、高性能场景下仍然有优势,tRPC 默认走 HTTP,微服务内部通信不一定比 gRPC 快。
结尾
回顾一下 API 技术的演进轨迹:REST 把 API 变成了资源,GraphQL 让客户端自己选数据,tRPC 让 TypeScript 成为 API 契约本身。
这三者不是谁取代谁的关系,而是不同场景各有各的用场。但在全栈 TypeScript 项目这个场景里,tRPC 带来的开发体验提升是实实在在的——少写一半 boilerplate,消灭一类运行时 bug,让类型为你工作。
还没试过 tRPC 的话,不妨在下一个 Next.js 项目里跑一遍上面的 demo。打通的那一刻,你大概也会脱口而出:「以前那些接口文档,写得可真够冤的。」
—
📖 推荐阅读
这些是跟前端工程师和大模型相关的文章,你可能会感兴趣:
React Compiler 1.0 来了:以后useMemo 和 useCallback 可以删了?
React 19 与 Vue 3.6:同一个2026年,两种不同的前端哲学
本地运行开源大模型指南:从Ollama到DeepSeek,手把手搭建你的私人AI