tRPC 快速开始 #
环境准备 #
在开始之前,请确保你的开发环境满足以下要求:
必需环境 #
text
┌─────────────────────────────────────────────────────────────┐
│ 环境要求 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Node.js: >= 18.x │
│ pnpm/npm/yarn: 最新稳定版 │
│ TypeScript: >= 4.7 │
│ │
│ 推荐开发工具: │
│ - VS Code │
│ - TypeScript 扩展 │
│ │
└─────────────────────────────────────────────────────────────┘
检查环境 #
bash
node -v
npm -v
npx tsc -v
项目初始化 #
方式一:使用脚手架(推荐) #
bash
create-trpc-appx my-trpc-app
选择项目类型:
text
? What type of project would you like to create?
❯ Next.js
Express
Standalone
方式二:手动安装 #
让我们从零开始手动配置一个 tRPC 项目:
bash
mkdir my-trpc-app
cd my-trpc-app
npm init -y
安装依赖 #
服务端依赖 #
bash
npm install @trpc/server zod
客户端依赖 #
bash
npm install @trpc/client @trpc/react-query @tanstack/react-query
开发依赖 #
bash
npm install -D typescript @types/node tsx
完整的 package.json #
json
{
"name": "my-trpc-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc",
"start": "node dist/server.js"
},
"dependencies": {
"@trpc/server": "^11.0.0",
"@trpc/client": "^11.0.0",
"@trpc/react-query": "^11.0.0",
"@tanstack/react-query": "^5.0.0",
"zod": "^3.22.0",
"express": "^4.18.0",
"cors": "^2.8.5"
},
"devDependencies": {
"@types/express": "^4.17.0",
"@types/cors": "^2.8.0",
"@types/node": "^20.0.0",
"typescript": "^5.0.0",
"tsx": "^4.0.0"
}
}
TypeScript 配置 #
创建 tsconfig.json:
json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
项目结构 #
text
my-trpc-app/
├── src/
│ ├── server/
│ │ ├── index.ts # 服务端入口
│ │ ├── trpc.ts # tRPC 初始化
│ │ └── routers/
│ │ ├── index.ts # 合并路由
│ │ └── user.ts # 用户路由
│ ├── client/
│ │ ├── index.ts # 客户端入口
│ │ └── trpc.ts # tRPC 客户端配置
│ └── shared/
│ └── types.ts # 共享类型
├── package.json
└── tsconfig.json
创建服务端 #
步骤 1:初始化 tRPC #
创建 src/server/trpc.ts:
typescript
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
export const t = initTRPC.create({
transformer: superjson,
});
export const router = t.router;
export const publicProcedure = t.procedure;
text
┌─────────────────────────────────────────────────────────────┐
│ tRPC 初始化 │
├─────────────────────────────────────────────────────────────┤
│ │
│ initTRPC.create() 创建 tRPC 实例 │
│ │
│ 主要导出: │
│ - router: 创建路由器 │
│ - procedure: 创建过程(API 端点) │
│ │
│ transformer: │
│ - superjson 支持更多类型(Date、Map、Set 等) │
│ - 可选配置 │
│ │
└─────────────────────────────────────────────────────────────┘
步骤 2:创建路由 #
创建 src/server/routers/user.ts:
typescript
import { z } from 'zod';
import { router, publicProcedure } from '../trpc';
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' },
];
export const userRouter = router({
list: publicProcedure.query(() => {
return users;
}),
getById: publicProcedure
.input(z.object({ id: z.number() }))
.query(({ input }) => {
return users.find((user) => user.id === input.id);
}),
create: publicProcedure
.input(
z.object({
name: z.string().min(1),
email: z.string().email(),
})
)
.mutation(({ input }) => {
const newUser = {
id: users.length + 1,
...input,
};
users.push(newUser);
return newUser;
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
name: z.string().optional(),
email: z.string().email().optional(),
})
)
.mutation(({ input }) => {
const index = users.findIndex((user) => user.id === input.id);
if (index === -1) {
throw new Error('User not found');
}
users[index] = { ...users[index], ...input };
return users[index];
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(({ input }) => {
users = users.filter((user) => user.id !== input.id);
return { success: true };
}),
});
步骤 3:合并路由 #
创建 src/server/routers/index.ts:
typescript
import { router } from '../trpc';
import { userRouter } from './user';
export const appRouter = router({
user: userRouter,
});
export type AppRouter = typeof appRouter;
text
┌─────────────────────────────────────────────────────────────┐
│ 路由合并 │
├─────────────────────────────────────────────────────────────┤
│ │
│ appRouter 结构: │
│ │
│ appRouter │
│ └── user │
│ ├── list -> user.list() │
│ ├── getById -> user.getById({ id: 1 }) │
│ ├── create -> user.create({ name, email }) │
│ ├── update -> user.update({ id, ... }) │
│ └── delete -> user.delete({ id }) │
│ │
│ AppRouter 类型导出: │
│ - 客户端需要此类型来获得类型安全 │
│ - 确保前后端类型一致 │
│ │
└─────────────────────────────────────────────────────────────┘
步骤 4:创建 HTTP 服务 #
创建 src/server/index.ts:
typescript
import express from 'express';
import cors from 'cors';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './routers';
import { createContext } from './context';
const app = express();
app.use(cors({ origin: 'http://localhost:3000' }));
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
})
);
app.listen(4000, () => {
console.log('Server running on http://localhost:4000');
});
步骤 5:创建上下文(可选) #
创建 src/server/context.ts:
typescript
import * as trpcExpress from '@trpc/server/adapters/express';
interface User {
id: string;
name: string;
}
export interface Context {
user: User | null;
}
export async function createContext({
req,
}: trpcExpress.CreateExpressContextOptions): Promise<Context> {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return { user: null };
}
return { user: { id: '1', name: 'User' } };
}
创建客户端 #
步骤 1:配置 tRPC 客户端 #
创建 src/client/trpc.ts:
typescript
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/routers';
export const trpc = createTRPCReact<AppRouter>();
步骤 2:创建客户端实例 #
创建 src/client/index.ts:
typescript
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/routers';
export const client = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:4000/trpc',
}),
],
});
async function main() {
const users = await client.user.list.query();
console.log('Users:', users);
const user = await client.user.getById.query({ id: 1 });
console.log('User:', user);
const newUser = await client.user.create.mutate({
name: 'Charlie',
email: 'charlie@example.com',
});
console.log('Created:', newUser);
}
main().catch(console.error);
text
┌─────────────────────────────────────────────────────────────┐
│ 客户端调用方式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Query(查询): │
│ client.router.procedure.query({ input }) │
│ │
│ Mutation(变更): │
│ client.router.procedure.mutate({ input }) │
│ │
│ 示例: │
│ // 获取用户列表 │
│ const users = await client.user.list.query(); │
│ │
│ // 获取单个用户 │
│ const user = await client.user.getById.query({ id: 1 }); │
│ │
│ // 创建用户 │
│ const newUser = await client.user.create.mutate({ │
│ name: 'John', │
│ email: 'john@example.com' │
│ }); │
│ │
└─────────────────────────────────────────────────────────────┘
React 集成 #
配置 Provider #
tsx
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { trpc } from './trpc';
const queryClient = new QueryClient();
function App() {
const [trpcClient] = React.useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: 'http://localhost:4000/trpc',
}),
],
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<UserList />
</QueryClientProvider>
</trpc.Provider>
);
}
使用 Hooks #
tsx
import { trpc } from './trpc';
function UserList() {
const usersQuery = trpc.user.list.useQuery();
const createUser = trpc.user.create.useMutation();
if (usersQuery.isLoading) {
return <div>Loading...</div>;
}
if (usersQuery.isError) {
return <div>Error: {usersQuery.error.message}</div>;
}
const handleCreate = async () => {
await createUser.mutateAsync({
name: 'New User',
email: 'new@example.com',
});
usersQuery.refetch();
};
return (
<div>
<h1>Users</h1>
<ul>
{usersQuery.data?.map((user) => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
<button onClick={handleCreate}>Create User</button>
</div>
);
}
text
┌─────────────────────────────────────────────────────────────┐
│ React Hooks │
├─────────────────────────────────────────────────────────────┤
│ │
│ Query Hooks: │
│ trpc.router.procedure.useQuery(input, options) │
│ │
│ Mutation Hooks: │
│ trpc.router.procedure.useMutation(options) │
│ │
│ 特性: │
│ ✅ 自动类型推断 │
│ ✅ 自动缓存 │
│ ✅ 自动重新获取 │
│ ✅ 乐观更新 │
│ │
└─────────────────────────────────────────────────────────────┘
Next.js 集成 #
项目结构 #
text
nextjs-trpc/
├── src/
│ ├── pages/
│ │ ├── api/
│ │ │ └── trpc/
│ │ │ └── [trpc].ts # API 路由
│ │ ├── _app.tsx # Provider 配置
│ │ └── index.tsx # 首页
│ └── server/
│ ├── routers/
│ │ └── index.ts
│ └── trpc.ts
└── package.json
API 路由 #
创建 src/pages/api/trpc/[trpc].ts:
typescript
import * as trpcNext from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers';
import { createContext } from '../../../server/context';
export default trpcNext.createNextApiHandler({
router: appRouter,
createContext,
});
App 配置 #
创建 src/pages/_app.tsx:
typescript
import type { AppType } from 'next/app';
import { trpc } from '../utils/trpc';
const MyApp: AppType = ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};
export default trpc.withTRPC(MyApp);
tRPC 配置 #
创建 src/utils/trpc.ts:
typescript
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../server/routers';
function getBaseUrl() {
if (typeof window !== 'undefined') {
return '';
}
if (process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}`;
}
return `http://localhost:${process.env.PORT ?? 3000}`;
}
export const trpc = createTRPCNext<AppRouter>({
config({ ctx }) {
return {
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
};
},
ssr: true,
});
运行项目 #
启动服务端 #
bash
npm run dev
测试 API #
bash
curl http://localhost:4000/trpc/user.list
curl -X POST http://localhost:4000/trpc/user.create \
-H "Content-Type: application/json" \
-d '{"0":{"json":{"name":"Test","email":"test@example.com"}}}'
调试技巧 #
使用 tRPC DevTools #
typescript
import { createTRPCReact } from '@trpc/react-query';
export const trpc = createTRPCReact<AppRouter>({
overrides: {
useMutation: {
async onSuccess(opts) {
console.log('Mutation success:', opts);
},
},
},
});
日志中间件 #
typescript
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
const loggerMiddleware = t.middleware(async ({ path, type, next }) => {
const start = Date.now();
const result = await next();
const duration = Date.now() - start;
console.log(`[${type}] ${path} - ${duration}ms`);
return result;
});
export const publicProcedure = t.procedure.use(loggerMiddleware);
常见问题 #
1. 类型推断不工作 #
确保正确导出 AppRouter 类型:
typescript
export type AppRouter = typeof appRouter;
2. CORS 错误 #
配置 CORS:
typescript
app.use(cors({
origin: 'http://localhost:3000',
credentials: true,
}));
3. 批量请求 #
tRPC 默认支持批量请求:
typescript
const [users, posts] = await Promise.all([
client.user.list.query(),
client.post.list.query(),
]);
下一步 #
现在你已经完成了 tRPC 的快速入门,接下来学习 路由器与过程,深入了解 tRPC 的核心概念!
最后更新:2026-03-29