跳至主要内容
版本:11.x

快速入门

tRPC 结合了来自 RESTGraphQL 的概念。如果您不熟悉其中任何一个,请查看关键 概念

安装

tRPC 分散在几个包中,因此您可以只安装您需要的包。确保在代码库的适当部分安装您想要的包。对于本快速入门指南,我们将保持简单,只使用原生客户端。有关框架指南,请查看 与 React 一起使用与 Next.js 一起使用

要求
  • tRPC 需要 TypeScript >= 4.7.0
  • 我们强烈建议您在 tsconfig.json 中使用 "strict": true,因为我们不正式支持非严格模式。

首先安装 @trpc/server@trpc/client

npm install @trpc/server@next @trpc/client@next

定义后端路由器

让我们逐步了解如何使用 tRPC 构建类型安全的 API。首先,此 API 将包含三个具有以下 TypeScript 签名的端点

ts
type User = { id: string; name: string; };
userList: () => User[];
userById: (id: string) => User;
userCreate: (data: { name: string }) => User;
ts
type User = { id: string; name: string; };
userList: () => User[];
userById: (id: string) => User;
userCreate: (data: { name: string }) => User;

1. 创建路由器实例

首先,让我们初始化 tRPC 后端。最好在单独的文件中执行此操作,并导出可重用的辅助函数,而不是整个 tRPC 对象。

server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
 
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;
server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
 
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;

接下来,我们将初始化我们的主路由器实例,通常称为 appRouter,我们将在其中添加过程。最后,我们需要导出路由器的类型,我们将在稍后的客户端使用它。

server/index.ts
ts
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
server/index.ts
ts
import { router } from './trpc';
 
const appRouter = router({
// ...
});
 
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;

2. 添加查询过程

使用 publicProcedure.query() 将查询过程添加到路由器。

以下创建了一个名为 userList 的查询过程,它从我们的数据库返回用户列表

server/index.ts
ts
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
// Retrieve users from a datasource, this is an imaginary database
const users = await db.user.findMany();
const users: User[]
return users;
}),
});
server/index.ts
ts
import { db } from './db';
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
// Retrieve users from a datasource, this is an imaginary database
const users = await db.user.findMany();
const users: User[]
return users;
}),
});

3. 使用输入解析器验证过程输入

要实现 userById 过程,我们需要接受来自客户端的输入。tRPC 允许您定义 输入解析器 来验证和解析输入。您可以定义自己的输入解析器,也可以使用您选择的验证库,例如 zodyupsuperstruct

您在 publicProcedure.input() 上定义输入解析器,然后可以在解析器函数中访问它,如下所示

输入解析器可以是任何 ZodType,例如 z.string()z.object()

server.ts
ts
import { z } from 'zod';
 
const appRouter = router({
// ...
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const input: string
// Retrieve the user with the given ID
const user = await db.user.findById(input);
const user: User | undefined
return user;
}),
});
server.ts
ts
import { z } from 'zod';
 
const appRouter = router({
// ...
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const input: string
// Retrieve the user with the given ID
const user = await db.user.findById(input);
const user: User | undefined
return user;
}),
});
信息

在本文档的其余部分,我们将使用 zod 作为我们的验证库。

4. 添加突变过程

与 GraphQL 类似,tRPC 区分查询和突变过程。

过程在服务器上的工作方式在查询和突变之间没有太大区别。方法名称不同,客户端使用此过程的方式也会发生变化 - 但其他一切都是一样的!

让我们通过将其作为路由器对象上的新属性添加来添加 userCreate 突变

server.ts
ts
const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create a new user in the database
const user = await db.user.create(input);
const user: { name: string; id: string; }
return user;
}),
});
server.ts
ts
const appRouter = router({
// ...
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const input: { name: string; }
// Create a new user in the database
const user = await db.user.create(input);
const user: { name: string; id: string; }
return user;
}),
});

提供 API

现在我们已经定义了路由器,我们可以提供它。tRPC 有许多 适配器,因此您可以使用您选择的任何后端框架。为了简单起见,我们将使用 standalone 适配器。

server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
 
const appRouter = router({
// ...
});
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from '@trpc/server/adapters/standalone';
 
const appRouter = router({
// ...
});
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
查看完整的后端代码
server/db.ts
ts
type User = { id: string; name: string };
 
// Imaginary database
const users: User[] = [];
export const db = {
user: {
findMany: async () => users,
findById: async (id: string) => users.find((user) => user.id === id),
create: async (data: { name: string }) => {
const user = { id: String(users.length + 1), ...data };
users.push(user);
return user;
},
},
};
server/db.ts
ts
type User = { id: string; name: string };
 
// Imaginary database
const users: User[] = [];
export const db = {
user: {
findMany: async () => users,
findById: async (id: string) => users.find((user) => user.id === id),
create: async (data: { name: string }) => {
const user = { id: String(users.length + 1), ...data };
users.push(user);
return user;
},
},
};

server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const router = t.router;
export const publicProcedure = t.procedure;
server/trpc.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const router = t.router;
export const publicProcedure = t.procedure;

server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { db } from "./db";
import { publicProcedure, router } from "./trpc";
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
const users = await db.user.findMany();
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
 
export type AppRouter = typeof appRouter;
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);
server/index.ts
ts
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { z } from "zod";
import { db } from "./db";
import { publicProcedure, router } from "./trpc";
 
const appRouter = router({
userList: publicProcedure
.query(async () => {
const users = await db.user.findMany();
return users;
}),
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
 
export type AppRouter = typeof appRouter;
 
const server = createHTTPServer({
router: appRouter,
});
 
server.listen(3000);

在客户端使用您的新后端

现在让我们转到客户端代码,并拥抱端到端类型安全性的力量。当我们导入 AppRouter 类型供客户端使用时,我们已经为我们的系统实现了完全的类型安全性,而不会将任何实现细节泄露给客户端。

1. 设置 tRPC 客户端

client/index.ts
ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
// 👆 **type-only** import
 
// Pass AppRouter as generic here. 👇 This lets the `trpc` object know
// what procedures are available on the server and their input/output types.
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});
client/index.ts
ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
// 👆 **type-only** import
 
// Pass AppRouter as generic here. 👇 This lets the `trpc` object know
// what procedures are available on the server and their input/output types.
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
}),
],
});

tRPC 中的链接类似于 GraphQL 中的链接,它们允许我们在数据发送到服务器 之前 控制数据流。在上面的示例中,我们使用 httpBatchLink,它会自动将多个调用批处理到单个 HTTP 请求中。有关链接的更深入使用,请参阅 链接文档

2. 查询和突变

您现在可以在 trpc 对象上访问您的 API 过程。试试看!

client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: { name: string; id: string; } | undefined
 
const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });
const createdUser: { name: string; id: string; }
client/index.ts
ts
// Inferred types
const user = await trpc.userById.query('1');
const user: { name: string; id: string; } | undefined
 
const createdUser = await trpc.userCreate.mutate({ name: 'sachinraja' });
const createdUser: { name: string; id: string; }

完整自动完成

您可以打开 Intellisense 在您的前端探索您的 API。您会发现所有过程路由都在等待您,以及用于调用它们的方法。

client/index.ts
ts
// Full autocompletion on your routes
trpc.u;
      
client/index.ts
ts
// Full autocompletion on your routes
trpc.u;
      

自己试试看!

下一步

提示

我们强烈建议您查看 示例应用程序,以了解如何在您最喜欢的框架中安装 tRPC。

提示

默认情况下,tRPC 会将复杂类型(如 Date)映射到它们的 JSON 等效项 (Date 的情况下为 string)。如果您想保留这些类型的完整性,最简单的方法是 使用 superjson 作为数据转换器。

tRPC 包含为 React 项目和 Next.js 设计的更复杂的客户端工具。