我是 Alex,或 GitHub 上的「KATT」,我想告訴你一個名為 tRPC 的程式庫。我沒有發布任何相關文章,所以我只是寫這篇簡介來開始進行(但我們在 GitHub 上已經不知不覺達到 >530 🌟)。期待文章和影片簡介陸續推出!如果你想隨時掌握最新資訊或想提問,可以在 Twitter 上追蹤我 @alexdotjs。
簡而言之,tRPC 為你提供從(節點)伺服器到客户端的端對端類型安全性,甚至不需要宣告類型。你在後端所做的就是傳回函式中的資料,而在前端則根據端點名稱使用所述資料。
執行 tRPC 端點和客户端呼叫時可能會像這樣:
我為 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 (<><inputplaceholder="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 (<><inputplaceholder="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 入門 中找到一些範例。