MUKI AI Summary
本文介紹如何使用 ts-rest 與 Zod 來建立型別安全的 API 合約及進行資料驗證。 ts-rest 是一個專為 TypeScript 設計的工具,能夠幫助開發者輕鬆定義和管理 API 合約,確保前後端的一致性。 Zod 則是一個資料驗證套件,能在執行階段驗證資料的完整性和正確性。文章提供了一個完整的前後端範例,後端使用 Node.js 搭配 Express 框架,前端使用 React.js,並詳細介紹了如何定義 API 路由和使用 Zod 驗證資料。
文章中描述的開發步驟包括:首先使用 ts-rest 定義 API 合約,確保每個 API 路由都有明確的類型定義;接著使用 Zod 驗證資料結構,確保符合預期格式。後端部分,使用 Express 框架建立 API 路由,並透過 ts-rest 將 API 合約與 Express Server 連接,實現資料處理的邏輯。前端則透過 ts-rest 呼叫後端 API,並展示如何在 React 中實作這些功能。文章最後提供了 CodeSandbox 的程式碼範例,供讀者進一步探索和測...
上一篇文章介紹了 ts-rest 以及與使用 TypeScript Interface 的差別,接下來會介紹,如何使用 ts-rest 建立型別安全的 API 合約,並使用 Zod 進行資料驗證。為了發揮 ts-rest 的最大作用,我會用完整的前後端範例,後端使用 Node.js 搭配 Express 框架,前端使用 React.js。
使用的工具與步驟
請容許我再次碎嘴介紹這兩個工具與他們的特性:
ts-rest
他是一個用於 TypeScript 的工具,可以幫助我們更輕鬆的定義和管理 API 合約,確保前後端的一致性。
這邊反覆提起的「合約」,在軟體開發中扮演關鍵角色,特別是前後端分離的專案架構中,以 ts-rest 為例,合約在此定義 API 的結構,確保資料格式一致。
Zod
Zod 是一個資料驗證的套件,我們用 zod 來定義資料結構,並且在 runtime (執行)階段就能進行驗證,確保資料的完整性和正確性。
預期開發步驟
這篇文章預期的開發步驟為:
1.定義 API 合約
我們會使用 ts-rest 來定義 API 路由,包含 request 以及 respond,並確保每個 API 路由都有明確的類型定義。
2.驗證資料
使用 Zod 定義資料結構,並驗證 request 和 respond 的資料,確保符合預期的格式和規範。
後端使用 ts-rest 定義 API 路由
安裝套件
▼ 我們需要安裝 typescript, ts-rest, 以及 Zod 相關套件
npm install express # @ts-rest/express 是給 Express 框架用的,可以參考 ts-rest 的官方文件看有支援哪些框架套件 npm install @ts-rest/core @ts-rest/express zod cors npm install --save-dev typescript @types/node @types/express
定義 API 合約與使用 Zod 進行資料驗證
接下來,我們在後端建立 API 合約,並透過 Zod 來進行資料結構的驗證。
▼ 定義 API 合約
import { initContract } from "@ts-rest/core"; import { z } from "zod"; const c = initContract(); const Schema = z.object({ id: z.string(), name: z.string(), age: z.number(), }); const ErrorSchema = z.object({ message: z.string(), }); // 定義 API 合約 export const apiContract = c.router({ getUser: { method: "GET", path: "/user/:id", responses: { 200: Schema, 404: ErrorSchema, }, }, });
我定義了一個 API 路徑 /user/:id
,它會根據用戶 ID 回傳使用者資料。如果找不到該使用者,則會回傳 404 Error。
將 API 合約連接到 Express Server
使用 createExpressEndpoints
將 API 合約 (apiContract
) 連到 Express Server,並讓 Server 處理指定路徑的 API 請求。我們要執行以下步驟:
- 定義路由邏輯 (
getUser
的處理函數)。 - 將這些處理函數與
ts-rest
API 合約對應。
這樣就能根據 apiContract
定義的 API 路由處理請求。
import { createExpressEndpoints, initServer } from "@ts-rest/express"; import express from "express"; import cors from "cors"; import bodyParser from "body-parser"; import { apiContract } from "./src/contract"; // 模擬資料庫的資料 const users = [ { id: "1", name: "MUKI", age: 18 }, { id: "2", name: "Apple", age: 25 }, ]; const app = express(); app.use(cors()) app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); const s = initServer(); // 定義 API handler(包含 API 邏輯處理函式的物件) // 我們定義了 getUser 的處理邏輯,根據 params.id 從一個模擬的資料庫中尋找使用者。如果找到則回傳 status code = 200 和使用者資料,否則回傳 404 和錯誤訊息。 const handlers = { getUser: async ({ params }: { params: { id: string } }) => { const user = users.find((u) => u.id === params.id); if (!user) { return { status: 404 as const, body: { message: "User not found" }, }; } else { return { status: 200 as const, body: user }; } }, }; // 連接 API 合約與 API 處理邏輯,並將其綁定到 Express 應用程式中 createExpressEndpoints(apiContract, handlers, app); // 啟動伺服器 app.listen(3000, () => { console.log("Server is running on http://localhost:3000"); });
啟動伺服器
伺服器啟動後,輸入 GET /user/:id
來呼叫 API。
▼ 以 CodeSandbox 為例,啟動後在網址列加入 /user/1
,就能取得模擬的使用者資料了。
前端使用 ts-rest 呼叫後端 API
打開 frontend 資料夾,安裝 react 與 @ts-rest/react-query 套件
npm install react @ts-rest/core @ts-rest/react-query
▼ 使用 ts-rest 建立 api.ts 檔案,並匯入後端寫好的 API 合約
import { initClient } from "@ts-rest/core"; // 因為我示範的前後端放在同個專案下,所以可用絕對路徑載入 apiContract import { apiContract } from "../../backend/src/contract"; export const api = initClient(apiContract, { // API 網址 baseUrl: "https://554vqy-3000.csb.app", });
▼ 在 App.tsx 呼叫 getUser,這邊只列出必要的程式碼
import { api } from "./api"; const fetchUser = async (id: string) => { try { const response = await api.getUser({ params: { id } }); if (response.status === 200) { setUser(response.body); setError(null); } } catch (err) { console.log(err); setError("Failed to fetch user"); setUser(null); } };
▼ 請參考實際執行畫面
到此,我們就做完了前後端基本的 ts-rest 串接。
CodeSandbox 程式碼範例
我將檔案完整範例放在 CodeSandbox 裡,大家可以打開來參考:https://codesandbox.io/p/github/mukiwu/ts-rest-demo/
不過因為使用 CodeSandbox 做完後端 API url,每次啟動 server 時,api 網址都不一樣,所以可能沒辦法直接測試呼叫 API,但可以將檔案 fork 到你自己的 CodeSandbox 試看看。
小結
這篇文章用簡單的範例介紹如何使用 ts-rest 和 Zod 來定義 API,希望能讓大家了解 ts-rest 的運作原理。下一篇預計會分享一些常見的問題,如果你在使用中有碰到問題也歡迎留言告訴我,也許能放進文章內與大家分享 XXD。