跳至主要內容
版本: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: 'https://127.0.0.1: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: 'https://127.0.0.1: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。