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进行检查。
- tRPC requires TypeScript >= 4.7.0
tRPC需要TypeScript >= 4.7.0 - We strongly recommend you using
"strict": true
in yourtsconfig.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
- yarn
- pnpm
- bun
npm install @trpc/server@next @trpc/client@next
yarn add @trpc/server@next @trpc/client@next
pnpm add @trpc/server@next @trpc/client@next
bun add @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.tsts
import {initTRPC } from '@trpc/server';/*** Initialization of tRPC backend* Should be done only once per backend!*/constt =initTRPC .create ();/*** Export reusable router and procedure helpers* that can be used throughout the router*/export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/trpc.tsts
import {initTRPC } from '@trpc/server';/*** Initialization of tRPC backend* Should be done only once per backend!*/constt =initTRPC .create ();/*** Export reusable router and procedure helpers* that can be used throughout the router*/export constrouter =t .router ;export constpublicProcedure =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.tsts
import {router } from './trpc';constappRouter =router ({// ...});// Export type router type signature,// NOT the router itself.export typeAppRouter = typeofappRouter ;
server/index.tsts
import {router } from './trpc';constappRouter =router ({// ...});// Export type router type signature,// NOT the router itself.export typeAppRouter = typeofappRouter ;
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.tsts
import {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({userList :publicProcedure .query (async () => {// Retrieve users from a datasource, this is an imaginary databaseconstusers = awaitdb .user .findMany ();returnusers ;}),});
server/index.tsts
import {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({userList :publicProcedure .query (async () => {// Retrieve users from a datasource, this is an imaginary databaseconstusers = awaitdb .user .findMany ();returnusers ;}),});
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函数上访问它,如下所示:
- Vanilla 香草
- Zod
- Yup
- Valibot 瓦利博特
server/index.tsts
constappRouter =router ({// ...userById :publicProcedure // The input is unknown at this time. A client could have sent// us anything so we won't assume a certain data type..input ((val : unknown) => {// If the value is of type string, return it.// It will now be inferred as a string.if (typeofval === 'string') returnval ;// Uh oh, looks like that input wasn't a string.// We will throw an error instead of running the procedure.throw newError (`Invalid input: ${typeofval }`);}).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server/index.tsts
constappRouter =router ({// ...userById :publicProcedure // The input is unknown at this time. A client could have sent// us anything so we won't assume a certain data type..input ((val : unknown) => {// If the value is of type string, return it.// It will now be inferred as a string.if (typeofval === 'string') returnval ;// Uh oh, looks like that input wasn't a string.// We will throw an error instead of running the procedure.throw newError (`Invalid input: ${typeofval }`);}).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
ZodType
, e.g. z.string()
or z.object()
.输入解析器可以是任何
ZodType
,例如 z.string()
或 z.object()
。server.tsts
import {z } from 'zod';constappRouter =router ({// ...userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server.tsts
import {z } from 'zod';constappRouter =router ({// ...userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
YupSchema
, e.g. yup.string()
or yup.object()
.server.tsts
import * asyup from 'yup';constappRouter =router ({// ...userById :publicProcedure .input (yup .string ().required ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server.tsts
import * asyup from 'yup';constappRouter =router ({// ...userById :publicProcedure .input (yup .string ().required ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
wrap
your schema with TypeSchema.server.tsts
import {wrap } from '@typeschema/valibot';import {string } from 'valibot';constappRouter =router ({// ...userById :publicProcedure .input (wrap (string ())).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server.tsts
import {wrap } from '@typeschema/valibot';import {string } from 'valibot';constappRouter =router ({// ...userById :publicProcedure .input (wrap (string ())).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
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.tsts
constappRouter =router ({// ...userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;// Create a new user in the databaseconstuser = awaitdb .user .create (input );returnuser ;}),});
server.tsts
constappRouter =router ({// ...userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;// Create a new user in the databaseconstuser = awaitdb .user .create (input );returnuser ;}),});
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.tsts
import {createHTTPServer } from '@trpc/server/adapters/standalone';constappRouter =router ({// ...});constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
server/index.tsts
import {createHTTPServer } from '@trpc/server/adapters/standalone';constappRouter =router ({// ...});constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
See the full backend code
查看完整的后端代码
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.tsts
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.consttrpc =createTRPCClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000',}),],});
client/index.tsts
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.consttrpc =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.tsts
// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'sachinraja' });
client/index.tsts
// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'sachinraja' });
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.tsts
// Full autocompletion on your routestrpc .u ;
client/index.tsts
// Full autocompletion on your routestrpc .u ;
Try it out for yourself!
自己试试吧!
Next steps 后续步骤
We highly encourage you to check out the example apps to learn about how tRPC is installed in your favorite framework.
我们强烈建议您查看示例应用程序,以了解 tRPC 如何安装在您喜欢的框架中。
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设计的更复杂的客户端工具。