这是用户在 2024-3-18 18:46 为 https://trpc.io/docs/quickstart 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?
Skip to main content
Version: 11.x 版本:11.x

Quickstart

tRPC combines concepts from REST and GraphQL. If you are unfamiliar with either, take a look at the key Concepts.
tRPC结合了REST和GraphQL的概念。如果您对其中任何一个都不熟悉,请查看关键概念。

Installation 安装

tRPC is split between several packages, so you can install only what you need. Make sure to install the packages you want in the proper sections of your codebase. For this quickstart guide we'll keep it simple and use the vanilla client only. For framework guides, checkout usage with React and usage with Next.js.
tRPC在几个软件包之间分割,因此您可以只安装您需要的软件包。请确保在代码库的适当部分安装所需的软件包。对于这个快速入门指南,我们将保持简单,只使用vanilla客户端。对于框架指南,请使用React和Next.js进行检查。

Requirements 要求
  • tRPC requires TypeScript >= 4.7.0
    tRPC需要TypeScript >= 4.7.0
  • We strongly recommend you using "strict": true in your tsconfig.json as we don't officially support non-strict mode.
    我们强烈建议您在 tsconfig.json 中使用 "strict": true ,因为我们不正式支持非严格模式。

Start off by installing the @trpc/server and @trpc/client packages:
首先安装 @trpc/server@trpc/client 包:

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

Defining a backend router
定义后端路由器

Let's walk through the steps of building a typesafe API with tRPC. To start, this API will contain three endpoints with these TypeScript signatures:
让我们逐步了解使用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. Create a router instance
1.创建路由器实例

First, let's initialize the tRPC backend. It's good convention to do this in a separate file and export reusable helper functions instead of the entire tRPC object.
首先,让我们初始化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;

Next, we'll initialize our main router instance, commonly referred to as appRouter, in which we'll later add procedures to. Lastly, we need to export the type of the router which we'll later use on the client side.
接下来,我们将初始化我们的主路由器实例,通常称为 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. Add a query procedure
2.添加查询过程

Use publicProcedure.query() to add a query procedure to the router.
使用 publicProcedure.query() 向路由器添加查询过程。

The following creates a query procedure called userList that returns a list of users from our database:
下面创建了一个名为 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. Using input parser to validate procedure inputs
3.使用输入解析器验证过程输入

To implement the userById procedure, we need to accept input from the client. tRPC lets you define input parsers to validate and parse the input. You can define your own input parser or use a validation library of your choice, like zod, yup, or superstruct.
为了实现 userById 过程,我们需要接受来自客户端的输入。tRPC允许您定义输入解析器来验证和解析输入。你可以定义自己的输入解析器或者使用你选择的验证库,比如zod,yup,或者superstruct。

You define your input parser on publicProcedure.input(), which can then be accessed on the resolver function as shown below:
publicProcedure.input() 上定义输入解析器,然后可以在resolver函数上访问它,如下所示:

The input parser can be any ZodType, e.g. z.string() or z.object().
输入解析器可以是任何 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;
}),
});
info 信息

Throughout the remaining of this documentation, we will use zod as our validation library.
在本文档的其余部分中,我们将使用 zod 作为验证库。

4. Adding a mutation procedure
4.添加一个突变过程

Similar to GraphQL, tRPC makes a distinction between query and mutation procedures.
与GraphQL类似,tRPC区分了查询和变异过程。

The way a procedure works on the server doesn't change much between a query and a mutation. The method name is different, and the way that the client will use this procedure changes - but everything else is the same!
过程在服务器上的工作方式在查询和变异之间不会有太大的变化。方法名不同了,客户端使用这个过程的方式也改变了--但其他一切都是一样的!

Let's add a userCreate mutation by adding it as a new property on our router object:
让我们添加一个 userCreate mutation,将它作为一个新属性添加到我们的router对象上:

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;
}),
});

Serving the API 为API提供服务

Now that we have defined our router, we can serve it. tRPC has many adapters so you can use any backend framework of your choice. To keep it simple, we'll use the standalone adapter.
现在我们已经定义了路由器,我们可以为它服务了。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);
See the full backend code
查看完整的后端代码
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);

Using your new backend on the client
在客户端上使用新的后端

Let's now move to the client-side code and embrace the power of end-to-end typesafety. When we import the AppRouter type for the client to use, we have achieved full typesafety for our system without leaking any implementation details to the client.
现在让我们转向客户端代码,并接受端到端类型封装的强大功能。当我们导入 AppRouter 类型供客户端使用时,我们已经为我们的系统实现了完整的类型安全,而没有向客户端泄露任何实现细节。

1. Setup the tRPC Client
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',
}),
],
});

Links in tRPC are similar to links in GraphQL, they let us control the data flow before being sent to the server. In the example above, we use the httpBatchLink, which automatically batches up multiple calls into a single HTTP request. For more in-depth usage of links, see the links documentation.
tRPC中的链接类似于GraphQL中的链接,它们让我们在发送到服务器之前控制数据流。在上面的示例中,我们使用了httpBatchLink,它自动将多个调用批处理为单个HTTP请求。有关链接的更深入用法,请参阅链接文档。

2. Querying & mutating
2.查询和变更

You now have access to your API procedures on the trpc object. Try it out!
现在,您可以访问 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; }

Full autocompletion 完全自动完成

You can open up your Intellisense to explore your API on your frontend. You'll find all of your procedure routes waiting for you along with the methods for calling them.
您可以打开您的智能感知,在前端探索您的API。您将发现所有的过程路由以及调用它们的方法都在沿着。

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

Try it out for yourself!
自己试试吧!​

Next steps 后续步骤

tip

We highly encourage you to check out the example apps to learn about how tRPC is installed in your favorite framework.
我们强烈建议您查看示例应用程序,以了解 tRPC 如何安装在您喜欢的框架中。

tip

By default, tRPC will map complex types like Date to their JSON-equivalent (string in the case of Date). If you want to add to retain the integrity of those types, the easiest way to add support for these is to use superjson as a Data Transformer.
默认情况下,tRPC 会将诸如 Date 之类的复杂类型映射到其 JSON 等效项(在 Date 的情况下为 string )。如果您想添加以保留这些类型的完整性,添加对这些类型的支持的最简单方法是使用 superjson 作为数据转换器。

tRPC includes more sophisticated client-side tooling designed for React projects and Next.js.
tRPC包括为React项目和Next.js设计的更复杂的客户端工具。