530 🌟 on GitHub). Expect articles & video intros to come! If you want to stay up-to-date or want to ask questions, you can follow me on Twitter at @alexdotjs.">530 🌟 on GitHub). Expect articles & video intros to come! If you want to stay up-to-date or want to ask questions, you can follow me on Twitter at @alexdotjs.">
跳到主要內容

介紹 tRPC

· 5 分鐘閱讀時間
Alex / KATT 🐱

我是 Alex,或 GitHub 上的「KATT」,我想告訴你一個名為 tRPC 的程式庫。我沒有發布任何相關文章,所以我只是寫這篇簡介來開始進行(但我們在 GitHub 上已經不知不覺達到 >530 🌟)。期待文章和影片簡介陸續推出!如果你想隨時掌握最新資訊或想提問,可以在 Twitter 上追蹤我 @alexdotjs

簡而言之,tRPC 為你提供從(節點)伺服器到客户端的端對端類型安全性,甚至不需要宣告類型。你在後端所做的就是傳回函式中的資料,而在前端則根據端點名稱使用所述資料。

執行 tRPC 端點和客户端呼叫時可能會像這樣: Alt Text

我為 React 製作了一個程式庫(@trpc/react),它建立在出色的 react-query 之上,但客户端程式庫(@trpc/client)可以在沒有 React 的情況下運作(如果你想建立特定的 Svelte/Vue/Angular/[..] 程式庫,請與我聯繫!)

不涉及任何程式碼產生,而且你可以很輕易地將它新增到現有的 Next.js/CRA/Express 專案中。

範例

以下是 tRPC 程序(又稱端點)的範例,稱為 hello,它接受 string 參數。

tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;
tsx
const appRouter = trpc.router().query('hello', {
input: z.string().optional(),
resolve: ({ input }) => {
return {
text: `hello ${input ?? 'world'}`,
};
},
});
export type AppRouter = typeof appRouter;

以下是使用所述資料的類型安全客户端

tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `https://127.0.0.1:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();
tsx
import type { AppRouter } from './server';
async function main() {
const client = createTRPCClient<AppRouter>({
url: `https://127.0.0.1:2022`,
});
const result = await client.query('hello', '@alexdotjs');
console.log(result); // --> { text: "hello @alexdotjs" }
}
main();

這就是您獲得類型安全所需的一切! result 是從後端在函式中傳回的內容推斷出來的類型。輸入的資料也從驗證器的傳回推斷出來,因此資料可以安全地直接使用 - 事實上,您必須通過驗證器傳遞輸入資料(& tRPC 可與 zod/yup/自訂驗證器立即使用)。

以下是一個 CodeSandbox 連結,您可以在其中使用上述範例: https://githubbox.com/trpc/trpc/tree/next/examples/standalone-server(查看終端機輸出,而不是預覽!)

什麼?我正在將程式碼從後端匯入到客戶端嗎? - 不,您實際上沒有

儘管看起來像這樣,但沒有程式碼從伺服器共用到客戶端;TypeScript 的 import type "[..] 僅匯入用於類型註解和宣告的宣告。它總是會被完全刪除,因此在執行階段沒有任何殘留。" - TypeScript 3.8 中新增的功能 - 請參閱 TypeScript 文件

沒有涉及程式碼產生,只要您有辦法從伺服器與客戶端共用類型,您今天就可以將此應用程式新增到您的應用程式(希望您已經在使用單一儲存庫)。

但我們才剛開始!

我之前提到有一個 React 函式庫,您可以在 React 中使用上述資料的方法

tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);
tsx
const { data } = trpc.useQuery(['hello', '@alexdotjs']);

.. 而且您將在客戶端取得類型安全的資料。

您今天可以使用現有的 brownfield 專案新增 tRPC(已取得 Express/Next.js 的轉接器)& 它與 CRA 搭配使用效果良好,也應該可以與 React Native 搭配使用。它甚至沒有與 React 綁定,因此如果您想要建立 Svelte 或 Vue 函式庫,請與我聯繫。

變異資料呢?

突變就像查詢一樣簡單,它們實際上在底層是相同的,但只是以語法糖的形式以不同的方式公開,並產生 HTTP POST 而不是 GET 請求。

以下是一個使用資料庫的稍微複雜一點的範例,取自我們的 TodoMVC 範例 todomvc.trpc.io / https://github.com/trpc/trpc/tree/next/examples/next-prisma-todomvc

tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});
tsx
const todoRouter = createRouter().mutation('add', {
input: z.object({
id: z.string().uuid(),
data: z.object({
completed: z.boolean().optional(),
text: z.string().min(1).optional(),
}),
}),
async resolve({ ctx, input }) {
const { id, data } = input;
const todo = await ctx.task.update({
where: { id },
data,
});
return todo;
},
});

React 用法如下所示

tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)
tsx
const addTask = trpc.useMutation('todos.add');
return (
<>
<input
placeholder="What needs to be done?"
onKeyDown={(e) => {
const text = e.currentTarget.value.trim();
if (e.key === 'Enter' && text) {
addTask.mutate({ text });
e.currentTarget.value = '';
}
}}
/>
</>
)

結束,暫時。

無論如何,正如我所說,我只是想讓球滾動起來。還有很多事情

  • 為使用者特定資料建立進入請求的內容,這些資料會注入相依性到解析器中 - 連結
  • 路由器的中間件支援 - 連結
  • 合併路由器(你可能不希望所有後端資料都在一個檔案中) - 連結
  • 使用我們的 @trpc/next 適配器在 React-land 中見過的最簡單的伺服器端渲染 - 連結
  • 類型安全的錯誤格式化 - 連結
  • 資料轉換器(透過網路使用 Date/Map/Set 物件) - 連結
  • React Query 的輔助程式

如果你想開始,可以在 Next.js 入門 中找到一些範例。

在 Twitter 上追蹤我以獲得更新!