首页
/ tRPC 端到端类型安全 API 开发指南:从零依赖架构到最小完整实现

tRPC 端到端类型安全 API 开发指南:从零依赖架构到最小完整实现

2026-09-05 21:03:57作者:段琳惟

tRPC("Move fast and break nothing. End-to-end typesafe APIs made easy.")是一个让你无需 schema 定义、无需代码生成,就能构建和消费完全类型安全 API 的 TypeScript 工具库。本文以官方 README.md 为主线,结合当前仓库(monorepo 版本 11.18.0)的源码与示例,带你完整理解 tRPC 的核心特性、Monorepo 组织结构、最小可运行的服务端/客户端实现,以及客户端 Links 链与服务器适配器的底层机制。读完本文,你将能够独立搭建一个带类型推导的 tRPC 服务,并知道在仓库中向哪里继续深挖。

一、tRPC 是什么:类型只导入,代码零拷贝

README 对 tRPC 的定位一句话概括:"tRPC allows you to easily build & consume fully typesafe APIs without schemas or code generation."——不用写 API schema(如 OpenAPI),不用跑 codegen 脚本,客户端就能获得输入、输出、错误三个维度的完整静态类型与自动补全。

这一能力的关键在于:客户端只从服务端导入类型声明,不导入任何运行时代码。README 中演示动画的图注明确强调:"The client above is not importing any code from the server, only its type declarations." 在仓库的最小示例中,这一模式体现得非常直白:

  • 服务端在 examples/minimal/src/server/index.ts 末尾导出 export type AppRouter = typeof appRouter;,它就是一个普通 TypeScript 类型;
  • 客户端在 examples/minimal/src/client/index.ts 中只写 import type { AppRouter } from '../server/index.js';——import type 在编译后即被擦除,浏览器/运行时不会加载任何服务端模块。

正因为类型来自 typeof appRouter,你在编辑器里重命名路由上的任意变量,类型错误会同时出现在前端和后端——这就是 README 特性列表中"Full static typesafety & autocompletion on the client, for inputs, outputs, and errors"的具体含义。

二、核心特性清单及仓库证据

README 列出了 8 项特性,下面逐条给出仓库内的对应证据:

  1. 经过充分测试、可用于生产packages/tests/server/ 下有约 40 个测试文件(如 batching.test.tswebsockets.test.tsstreaming.test.ts),覆盖批处理、WebSocket、流式、错误格式化等路径;根目录 vitest.config.ts 统一管理测试。
  2. 完整静态类型安全:客户端类型由 DecoratedProcedureRecord 从路由记录递归推导(见下文第四节源码分析)。
  3. 无需代码生成(Snappy DX):所有包都直接用 tsdown 构建(各包 package.json"build": "tsdown"),没有 schema 编译步骤。
  4. 零依赖、客户端足迹小:可以核实 packages/server/package.jsonpackages/client/package.json——两者都没有 dependencies 字段,仅有 devDependenciespeerDependenciestypescript >=5.7.2),README"zero deps"的说法与包清单一致。
  5. 易于加入既有项目(brownfield):每个框架都有独立适配器入口,按需引入即可(见第六节)。
  6. 自带适配器(React/Next/Express/Fastify 等)packages/server/src/adapters/ 下包含 express.tsfastify/fetch/next.tsnext-app-dir.tsaws-lambda/standalone.tsws.ts 等,客户端侧另有 React 生态集成包 packages/react-query/packages/tanstack-react-query/、Next.js 集成包 packages/next/
  7. Subscriptions(订阅)支持:客户端有 httpSubscriptionLinkwsLink/,服务端有 adapters/ws.tswsEncoder.ts,示例 examples/next-prisma-websockets-starter/ 提供完整 WebSocket 场景。
  8. 请求批处理(Request batching):同一时刻发出的多个请求可自动合并为一次 HTTP 请求,实现于 httpBatchStreamLink,并有专门测试 packages/tests/server/batching.test.ts 验证行为。

三、Monorepo 组织:pnpm workspace + Turbo + Lerna

当前仓库是一个 pnpm workspace + Turborepo + Lerna 的 Monorepo,版本由 lerna.json 统一管理("version": "11.18.0""npmClient": "pnpm")。工作区成员定义在 pnpm-workspace.yaml

packages:
  - 'packages/*'
  - 'examples/*'
  - 'examples/.*/*'
  - 'www'
  - 'www/og-image'

核心包结构(packages/ 下):

发布名 职责
packages/server @trpc/server 服务端核心:initTRPC、procedure、router、全部 HTTP/Lambda 适配器
packages/client @trpc/client 运行时无关的客户端:createTRPCClient 与 Links 链
packages/react-query React Query 集成 createTRPCReact 等(经典集成)
packages/tanstack-react-query TanStack 集成 新的 TanStack Query 集成(见仓库博客 www/blog/2025-02-17-new-tanstack-react-query-integration.mdx
packages/next Next.js 集成 createTRPCNext、SSR prepass
packages/openapi OpenAPI 支持 从 tRPC 路由生成 OpenAPI 文档
packages/upgrade 升级工具 v10 → v11 代码迁移
packages/tests 跨包集成测试

根目录 package.json 声明了开发环境基线:Node ^24.0.0、pnpm ^10.33.1packageManager: pnpm@10.33.1)、TypeScript ^5.9.2、zod ^4.2.1,常用脚本如 pnpm buildturbo --filter=./packages/* build)、pnpm devpnpm test(vitest)、pnpm lint

四、快速上手:最小 tRPC 服务(examples/minimal)

仓库提供了 examples/minimal 作为"最小可工作示例"(要求 Node 18+,因需全局 fetch;运行 npm i && npm run dev)。下面按其完整源码拆解服务端、共享层、客户端三层。

4.1 服务端:初始化 tRPC 实例

examples/minimal/src/server/trpc.ts

import { initTRPC } from '@trpc/server';
import { transformer } from '../shared/transformer.js';

/**
 * Initialization of tRPC backend
 * Should be done only once per backend!
 */
const t = initTRPC.create({
  transformer,
});

/**
 * Export reusable router and procedure helpers
 * that can be used throughout the router
 */
export const router = t.router;
export const publicProcedure = t.procedure;

要点:initTRPC.create() 每个后端进程只应调用一次;它产出的 t.router / t.procedure 是后续所有路由的构建块。这里的 transformer 来自共享层,用于处理 DateTemporalDecimal.js 等 JSON 无法直接表达的类型。

4.2 共享层:数据转换器

examples/minimal/src/shared/transformer.ts

import superjson from 'superjson';

export const transformer = superjson;

注意注释中的约定:服务端和客户端都应导入这个共享文件,而不是各自直接导入 superjson,以保证两端序列化规则一致。示例使用 superjson 是可选约定,不传 transformer 时走原生 JSON。

4.3 路由定义:query / mutation / 可迭代输出

examples/minimal/src/server/index.ts(假数据库见 examples/minimal/src/server/db.ts):

import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { z } from 'zod';
import { db } from './db.js';
import { publicProcedure, router } from './trpc.js';

const appRouter = router({
  user: {
    list: publicProcedure.query(async () => {
      const users = await db.user.findMany();
      return users;
    }),
    byId: publicProcedure.input(z.string()).query(async (opts) => {
      const { input } = opts;
      const user = await db.user.findById(input);
      return user;
    }),
    create: publicProcedure
      .input(z.object({ name: z.string() }))
      .mutation(async (opts) => {
        const { input } = opts;
        const user = await db.user.create(input);
        return user;
      }),
  },
  examples: {
    iterable: publicProcedure.query(async function* () {
      for (let i = 0; i < 3; i++) {
        await new Promise((resolve) => setTimeout(resolve, 500));
        yield i;
      }
    }),
  },
});

// Export type router type signature, this is used by the client.
export type AppRouter = typeof appRouter;

const server = createHTTPServer({
  router: appRouter,
});

server.listen(3000);

这段代码覆盖了 tRPC 服务端的三大要素:

  • 命名空间式路由user.listuser.byIduser.create 由嵌套对象结构自然形成,客户端调用路径与之逐字对应;
  • zod 输入校验.input(z.string()) / .input(z.object({ name: z.string() })) 同时承担运行时校验与类型推导两职——opts.input 被推断为具体类型,create 的入参被推断为 { name: string }
  • 生成器即流式输出examples.iterableasync function* 直接 yield 数据,客户端可 for await 消费,展示了 tRPC 对流式/迭代器输出的原生支持;
  • 适配器接入createHTTPServer 来自子路径导出 @trpc/server/adapters/standalone,两行代码即得到一个监听 3000 端口的 HTTP 服务。

4.4 客户端:只导类型 + Links 路由

examples/minimal/src/client/index.ts

import {
  createTRPCClient,
  httpBatchStreamLink,
  httpSubscriptionLink,
  splitLink,
} from '@trpc/client';
/**
 * We only import the `AppRouter` type from the server - this is not available at runtime
 */
import type { AppRouter } from '../server/index.js';
import { transformer } from '../shared/transformer.js';

const trpc = createTRPCClient<AppRouter>({
  links: [
    splitLink({
      condition: (op) => op.type === 'subscription',
      true: httpSubscriptionLink({
        url: 'http://localhost:3000',
        transformer,
      }),
      false: httpBatchStreamLink({
        url: 'http://localhost:3000',
        transformer,
      }),
    }),
  ],
});

async function main() {
  const users = await trpc.user.list.query();
  console.log('Users:', users);

  const createdUser = await trpc.user.create.mutate({ name: 'sachinraja' });
  console.log('Created user:', createdUser);

  const user = await trpc.user.byId.query('1');
  console.log('User 1:', user);

  const iterable = await trpc.examples.iterable.query();
  for await (const i of iterable) {
    console.log('Iterable:', i);
  }
}

void main();

源码注释中给出了三条"体验类型安全"的提示:悬停查看推导出的类型、Cmd/Ctrl+点击跳转到定义、重命名后前后端同步报错。注意方法名的映射规则:query 类用 .query(),mutation 类用 .mutate(),subscription 类用 .subscribe(),与路由定义时 t.procedure.query/mutation/subscription 一一对应。

五、客户端架构:Proxy + Links 链的源码解读

README 强调"无运行时膨胀",其秘密在于客户端极薄。阅读 packages/client/src/createTRPCClient.ts 可以看到完整机制:

  1. 类型推导TRPCClient<TRouter> 类型通过 DecoratedProcedureRecord 递归遍历路由记录;其中 DecorateProcedurecreateTRPCClient.ts#L78-L93)按 procedure 的 type 字段决定挂哪个方法——query → querymutation → mutatesubscription → subscribe,入参用 inferProcedureInput、返回值用 inferTransformedProcedureOutput 推导。
  2. 运行时实现createTRPCClientcreateTRPCClient.ts#L160-L166)内部先构造 TRPCUntypedClient,再包一层 createRecursiveProxycreateTRPCClient.ts#L141-L158)。代理截取访问路径 ['user','byId','query'],把最后一段映射为 procedure 类型,前段拼成 user.byId 作为 RPC 路径,最终调用 client.query('user.byId', ...)。也就是说,trpc.user.byId.query('1') 在运行时只是一次路径拼接 + 方法分派,没有反射、没有 schema 查找表。
  3. Links 链是请求管道links 目录(packages/client/src/links)包含 httpBatchLink.tshttpBatchStreamLink.tshttpLink.tshttpSubscriptionLink.tssplitLink.tsretryLink.tsloggerLink.tslocalLink.tswsLink/。示例中的 splitLink 按操作类型分流:订阅走 httpSubscriptionLink(SSE 长连接),其余走 httpBatchStreamLink(批处理 + 流式响应)。这也印证了 README 特性中"批处理"与"订阅"两项并非营销话术,而是有独立 Link 实现与测试支撑。
  4. 包边界packages/client/src/index.ts 导出 createTRPCClientTRPCClientError、全部 links,并把旧 API createTRPCProxyClient / inferRouterProxyClient 标记为 @deprecated(v12 移除)——从 v11 起官方客户端不再带"proxy"字样,语义上强调其并非对服务端的运行时代理,而只是类型镜像。

另外注意 packages/client/package.jsonpeerDependencies 中锁定了同版本的 @trpc/server(11.18.0):客户端的类型推导依赖服务端的类型定义,两者必须版本配套。

六、服务端:子路径导出与适配器矩阵

@trpc/serverexports 字段(见 packages/server/package.json)定义了清晰的模块化边界,按需引入:

子路径 用途 示例场景
@trpc/server/adapters/standalone 原生 Node HTTP 服务 本指南最小示例
@trpc/server/adapters/express / fastify / fetch / node-http 框架集成 examples/express-minimal/examples/fastify-server/
@trpc/server/adapters/next / next-app-dir Next.js Pages Router / App Router API Route examples/next-minimal-starter/examples/next-prisma-starter/
@trpc/server/adapters/aws-lambda AWS Lambda + API Gateway examples/lambda-api-gateway/examples/lambda-url/
@trpc/server/adapters/ws WebSocket 订阅 examples/next-prisma-websockets-starter/
@trpc/server/observable 自管订阅/流(Observable 原语) 自定义 Link 开发
@trpc/server/rpc/shared 共享的 RPC 载荷与错误类型 协议层分析

入口文件 packages/server/src/index.ts 只有 export * from './@trpc/server',核心实现(initTRPC.tsprocedure.tsrouter.tsmiddleware.ts 等)位于 packages/server/src/unstable-core-do-not-import/ 目录——目录名本身是明确的维护契约:外部项目不应直接引用该内部模块,只应走公开 API。

七、快速启动:用官方示例脚手架建项目

README 提供了基于 Next.js 全栈示例(examples/next-prisma-starter,内置 Prisma、vitest、Playwright)的一键脚手架命令,覆盖五种包管理器:

# yarn
yarn create next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# npm
npx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# pnpm
pnpm create next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# bun
bunx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# deno
deno init --npm next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

除了 Next.js,examples/ 目录还按场景组织了大量可运行示例,选型时可对照:

  • 纯后端express-minimalfastify-serverstandalone-serverbundeno-deploycloudflare-workers
  • 服务端架构soa(多服务 + 网关)、lazy-load(路由懒加载)、openapi-codegen(OpenAPI 生成与校验);
  • Next.js 系列next-minimal-starternext-edge-runtimenext-formdatanext-sse-chat(SSE 聊天)、next-websockets-encodernext-prisma-todomvcnext-prisma-websockets-starter
  • 前端最小集minimal(本指南所用)、minimal-react(React + Vite)、minimal-content-types(非 JSON 内容类型);
  • 服务端lesslambda-api-gatewaylambda-api-gateway-streaminglambda-urlvercel-edge-runtime

八、面向 AI Agent 的开发支持

README 单独列出了 AI Agents 章节:若使用 Claude Code、Cursor、Windsurf 等 AI 编码代理,可安装 tRPC 官方 skills 以获得更贴合库约定的代码生成:

npx @tanstack/intent@latest install

这一机制在仓库中可找到落地证据:根目录 package.json 依赖了 @tanstack/intent,且 @trpc/server@trpc/client 等包的 bin 字段都暴露了 intent 可执行文件,各包 package.jsonfiles 字段中包含 skills 目录——例如 packages/server/skills 下有 server-setuptrpc-routermiddlewaresvalidatorssubscriptionsadapter-aws-lambda 等主题化的技能说明,packages/client/skills 下则有 client-setuplinkssuperjson。安装后 AI 代理在生成 tRPC 代码时会参考这些技能文档,减少"凭印象写 tRPC"的偏差。

九、深入阅读路径

沿着本文脉络,推荐按以下顺序在仓库内继续深挖:

  1. 协议层packages/server/src/rpc.tspackages/server/src/http.ts——理解 tRPC 的 JSON 载荷格式(?input= 编码、[ok, data] 响应结构);
  2. 服务端核心packages/server/src/unstable-core-do-not-import/initTRPC.tsprocedureBuilder.ts——procedure 构建链(input → use 中间件 → query/mutation)如何组装;
  3. 行为测试packages/tests/server/input.test.ts(zod 校验失败路径)、errorFormatting.test.tswebsockets.memory.test.ts(内存 WebSocket 全链路);
  4. 迁移指南:官方 v10 → v11 迁移文档 www/docs/migration/migrate-from-v10-to-v11.mdx 与配套的 packages/upgrade 自动迁移包;
  5. 概念文档www/docs/further/rpc.mdwww/docs/main/concepts.mdx

适用前提小结:本文基于当前仓库 11.18.0 版本源码;monorepo 本地开发要求 Node 24 + pnpm 10.33,而独立示例(如 examples/minimal)只需 Node 18+;@trpc/client@trpc/server 需同版本使用(peerDependency 约束),TypeScript 要求 >=5.7.2

登录后查看全文
热门项目推荐
相关项目推荐