frontend + backend 中間使用 openapi

在前後端開發的架構中,使用 OpenAPI ( 前身為 Swagger )作為溝通橋樑已經是非常主流的做法。

什麼是 OpenAPI

簡單來說, OpenAPI 就像是一份規格說明書合約。前後端工程師在動工之前,會先用這份說明書定義好所有的 API 網址、傳輸的資料格式以及錯誤回應。當雙方達成共識後,再各自依照這份合約去寫程式。

使用 OpenAPI 的好處

1. 前後端開發互不打擾

在傳統開發中,前端通常需要等待後端把 API 寫好、架設好伺服器,才能開始串接測試。

使用 OpenAPI 後,前端工程師可以根據合約,利用工具自動生成假資料( Mock Data )。如此一來,前後端就能同時開工,大幅縮短專案的開發時間。

2. 自動生成程式碼與文件

手寫 API 文件既花時間又容易出錯。 OpenAPI 支援許多自動化工具,可以直接將合約轉換成漂亮的網頁文件。

更厲害的是,它還能自動生成前端的呼叫函式與後端的 API 路由架構,減少工程師手動複製貼上欄位名稱的低級錯誤。

3. 單一事實來源

當規格發生變更時,大家都以 OpenAPI 的那份檔案為準。這能有效避免「 後端改了欄位名稱,卻忘記通知前端 」導致程式出錯的溝通悲劇。

使用 OpenAPI 的缺點與挑戰

1. 前期的學習與溝通成本

撰寫 OpenAPI 需要學習特定的語法( 通常是 YAML 或 JSON 格式 )。在開發初期,前後端工程師需要花費不少時間開會討論,逐一確認每個欄位的型態與定義,這會讓專案初期的進度看起來比較緩慢。

2. 規格與實際程式碼脫節的風險

如果團隊成員在修改程式碼時,沒有同步更新 OpenAPI 檔案,這份合約就會失去信任度。雖然有工具可以進行自動化檢查,但這依然高度仰賴團隊的開發紀律。

3. 調整彈性降低

一旦 OpenAPI 規格定案且雙方開始開發,如果中途想要大幅度修改設計,就會牽一髮而動全身。前端、後端以及文件都需要同步調整,修改的成本會比直接口頭溝通、隨意改 Code 還要高。

總結

對於小型、一兩天就能完成的臨時專案,使用 OpenAPI 可能會顯得有些繁瑣。

但是對於需要長期維護、團隊人數較多,或是前後端分離的現代網頁專案, OpenAPI 帶來的功能自動化與溝通效率,完全足以彌補它帶來的初期成本。


如果你想在專案中開始使用 OpenAPI 並且建立前後端的欄位對應( Mapping ),可以按照以下步驟循序漸進地開始。

如何開始使用 OpenAPI ( Getting Started )

要開始使用 OpenAPI ,最推薦的方法是採用設計優先( Design-First )的流程。也就是在寫程式碼之前,先動手寫規格書。

第一步:安裝與準備工具

你不需要從零開始用純文字編輯器苦讀語法,可以善用以下免費工具:

  • Swagger Editor: 這是最經典的線上編輯器,左邊寫語法( 通常使用 YAML 格式 ),右邊就會即時渲染出漂亮的 API 文件網頁。
  • VS Code 套件: 如果你使用 VS Code ,可以搜尋並安裝名為 OpenAPI (Swagger) EditorSwagger Viewer 的擴充套件,在本地端就能直接編寫。

第二步:撰寫第一個 API 規格

一個基礎的 OpenAPI 檔案會包含三個核心部分:

  1. 資訊( Info ): API 的名稱、版本。
  2. 路徑( Paths ): 網址與請求方法( 例如 GET /users )。
  3. 元件( Components ): 定義資料結構的地方。

以下是一個最簡單的 YAML 範例:

YAML

openapi: 3.0.0
info:
  title: 學生資料 API
  version: 1.0.0
paths:
  /students/{id}:
    get:
      summary: 取得特定學生資料
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 成功取得資料
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Student'
components:
  schemas:
    Student:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string

如何做前後端欄位對應( How to Make Mapping )

在 OpenAPI 的世界裡,前後端的欄位對應主要是透過 components/schemas ( 組件模型 )來實現。這就像是定義好一個資料夾,讓前端和後端都認同這個資料夾裡裝的東長什麼樣子。

要做好欄位對應,可以遵循以下三個關鍵步驟:

1. 使用 Schema 定義標準資料模型

components/schemas 下定義好你要對應的模型名稱。例如,後端資料庫有一個使用者資料表,你就在這裡定義一個 User 模型。

YAML

components:
  schemas:
    User:
      type: object
      required:
        - userId
        - userName
      properties:
        userId:
          type: integer
          description: 使用者的唯一識別碼
        userName:
          type: string
          description: 使用者的顯示名稱
        email:
          type: string
          format: email

2. 透過引用( $ref )進行對應

定義好模型後,不論是後端收到的請求( Request Body ),或是前端收到的回應( Response Body ),只要遇到這組資料,就統一使用 $ref 去引用剛才定義好的模型:

YAML

# 在 API 回應中引用
responses:
  '200':
    description: 成功
    content:
      application/json:
        schema:
          $ref: '#/components/schemas/User'

透過這種方式,前後端的資料規格就牢牢地綁定在一起,完成了對應。

3. 利用自動化工具生成前後端程式碼

當規格書寫好、對應關係確立後,不需要人工去對照著寫程式。你可以使用 OpenAPI Generator 這個工具。

  • 後端對應: 可以自動生成對應程式語言的模型類別( 例如 Java 的 DTO 、 TypeScript 的 Interface 或 Python 的 Pydantic Model )。後端工程師直接將資料庫的資料轉成這個類別即可。
  • 前端對應: 可以自動生成呼叫 API 的 Fetch 或 Axios 函式庫,並且自帶完整的型別提示。前端工程師在寫程式時,輸入 user. 就會自動跳出 userIduserName 的欄位提示,完全不用擔心拼字錯誤。

透過這樣由規格書主導的自動化對應,就能確保前後端傳輸的資料永遠保持同步與一致。


要讓前端的 React 和後端的 Python 完美串接並確保欄位正確,最核心的做法就是透過 OpenAPI 檔案來自動生成雙端的型別與程式碼( 套件 )

以下是完整的實作流程與工具推薦。

步驟一:後端 Python 的自動生成與檢查

在 Python 中,你不需要自己手寫 YAML 規格書。最推薦使用 FastAPI 框架,它天生與 OpenAPI 整合。

1. 使用 Pydantic 定義資料模型

FastAPI 透過 Pydantic 來做資料驗證。你只需要寫好 Python 的 Class ,它就會自動幫你做前端傳進來的資料檢查。

Python

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

# 定義資料模型,這會自動變成 OpenAPI 的 Schema
class UserSchema(BaseModel):
    user_id: int
    user_name: str
    email: EmailStr

@app.post("/users", response_model=UserSchema)
def create_user(user: UserSchema):
    # 如果前端傳入的資料型態不對, FastAPI 會自動回傳 422 錯誤,完全不用寫 if-else 檢查
    return user

2. 自動生成 OpenAPI JSON 檔案

當你啟動 FastAPI 伺服器後,直接瀏覽 http://localhost:8000/openapi.json ,後端就已經根據你的 Python 程式碼,自動產生了完美的 OpenAPI 規格書。

步驟二:如何使用 OpenAPI 產生前端 React 套件

有了後端產生的 openapi.json 後,接下來就要利用工具,把它變成 React 可以直接 import 的前端套件( SDK )。

這裡推薦使用目前社群最流行的工具: @hey-api/openapi-ts ( 前身為 openapi-typescript-codegen )。它可以把 OpenAPI 檔案轉換成帶有完整 TypeScript 型別的 Axios 或 Fetch 函式庫。

1. 在 React 專案中安裝工具

請在你的 React 專案目錄下執行以下指令:

Bash

npm install @hey-api/openapi-ts --save-dev

2. 建立設定檔

在 React 專案根目錄建立一個名為 openapi-ts.config.ts 的檔案,告訴工具你的後端 API 規格書在哪裡,以及要把套件生成到哪個資料夾:

TypeScript

import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'http://localhost:8000/openapi.json', // 後端的 OpenAPI 網址
  output: 'src/client',                         // 產生的前端套件放置路徑
  client: 'axios',                              // 使用 Axios 作為底層連線工具
});

3. 執行生成指令

package.jsonscripts 中加入一行指令:

JSON

"scripts": {
  "generate-client": "openapi-ts"
}

接著在終端機輸入並執行:

Bash

npm run generate-client

執行完畢後,你的 src/client 資料夾下就會多出自動生成好的 API 呼叫函式與完整的 TypeScript 型別定義。

步驟三:在 React 中享受自動檢查的好處

當套件生成完畢後,前端工程師在 React 裡面呼叫後端 API 就變得無比輕鬆,而且編輯器會自動幫你檢查欄位是否正確。

TypeScript

import React, { useEffect, useState } from 'react';
// 直接從剛剛生成的套件中引入服務與型別
import { UserService, UserSchema } from './client';

export const UserComponent = () => {
  const [user, setUser] = useState<UserSchema | null>(null);

  const handleCreateUser = async () => {
    try {
      // 這裡會有完全正確的欄位提示,打錯字或型態不對, React 邊譯時就會直接報錯
      const response = await UserService.createUser({
        requestBody: {
          user_id: 123,
          user_name: '杰哥',
          email: '[email protected]'
        }
      });
      setUser(response);
    } catch (error) {
      console.error('資料格式不符或其他錯誤', error);
    }
  };

  return (
    <button onClick={handleCreateUser}>建立使用者</button>
  );
};

總結工作流程

  1. 後端 Python( FastAPI ): 負責寫業務邏輯與 Pydantic 模型,啟動後自動產出 openapi.json
  2. 前端 React( OpenAPI Generator / Hey API ): 讀取後端的 openapi.json ,一鍵生成前端的 API 請求套件。
  3. 雙端檢查: 前端程式碼在編譯時會被 TypeScript 嚴格檢查欄位;後端 API 在運行時會被 Pydantic 自動攔截錯誤格式。雙重把關下,前後端串接絕對不會出錯。

如果後端換成 Go 語言( Golang ),實作邏輯也是完全一樣的!我們一樣可以透過工具,達到「 自動生成 OpenAPI 規格書 」與「 後端自動驗證欄位 」的效果。

在 Go 語言的生態系中,最主流、最推薦的作法是使用 Gin 框架 搭配 swag 套件( 實作 OpenAPI 2.0/3.0 規範 )。

以下是 Go 語言後端如何與 React 串接的完整步驟。

步驟一:後端 Go 語言的自動生成與檢查

Go 語言是強型別語言,我們會利用結構體( struct )來定義資料模型,並透過註解( Comment )讓工具自動產出 OpenAPI 檔案。

1. 安裝 Swag 工具

首先,在你的電腦中安裝將註解轉換為 Swagger 文件的工具:

Bash

go install github.com/swaggo/swag/cmd/swag@latest

2. 在 Go 程式碼中定義模型與註解

在你的 Go 專案中( 假設使用 Gin 框架 ),你可以這樣寫:

Go

package main

import (
	"net/http"
	"github.com/gin-gonic/gin"
	_ "myproject/docs" // 這行很重要,用來引入自動生成的 Swagger 文件資料
)

// UserSchema 定義了傳入與傳出的資料結構
// binding:"required" 標籤會讓 Gin 自動檢查欄位是否存在,型態不對就會報錯
type UserSchema struct {
	UserID   int    `json:"user_id" binding:"required" example:"123"`
	UserName string `json:"user_name" binding:"required" example:"杰哥"`
	Email    string `json:"email" binding:"required,email" example:"[email protected]"`
}

// CreateUser 處理建立使用者的路由
// @Summary 建立使用者
// @Description 傳入使用者資料並建立新用戶
// @Accept json
// @Produce json
// @Param user body UserSchema true "使用者資料"
// @Success 200 {object} UserSchema
// @Router /users [post]
func CreateUser(c *gin.Context) {
	var input UserSchema
	
	// ShouldBindJSON 會自動檢查前端傳來的資料格式是否符合 UserSchema 的規範
	if err := c.ShouldBindJSON(&input); err != nil {
		// 如果驗證失敗(例如少給欄位、Email格式不對),直接回傳 400 錯誤與原因
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 驗證成功,回傳資料
	c.JSON(http.StatusOK, input)
}

func main() {
	r := gin.Default()
	r.POST("/users", CreateUser)
	r.Run(":8000")
}

3. 一鍵生成 openapi.json

當你在程式碼中寫好註解後,在專案根目錄執行以下指令:

Bash

swag init

執行後, Go 工具會在你的專案下建立一個 docs 資料夾,裡面就會包含自動產生的 swagger.json ( 即 OpenAPI 規格書 )。當你啟動 Go 伺服器後,這個檔案就可以透過網址( 例如 http://localhost:8000/docs/doc.json )讓外界讀取。

步驟二:前端 React 的套件生成( 流程完全不變 )

不論後端是用 Python、Go 還是 Java,只要後端產出了 OpenAPI 的 JSON 檔案,前端 React 的步驟是完全一模一樣的。

前端的 @hey-api/openapi-ts 工具只需要讀取 Go 後端產生的網址即可:

TypeScript

// openapi-ts.config.ts
import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'http://localhost:8000/docs/doc.json', // 換成 Go 後端提供的規格書網址
  output: 'src/client',
  client: 'axios',
});

接著在 React 專案下執行:

Bash

npm run generate-client

React 就會拿到跟之前一模一樣的 TypeScript 型別檔案與 API 呼叫函式。

總結 Go 語言環境下的工作流程

  1. 後端 Go ( Gin + Swag ): 利用 structtag( 如 binding:"required,email" )來做運行時的資料正確性檢查。
  2. 產出規格書: 執行 swag init,將程式碼中的註解轉換成 OpenAPI 的 JSON 檔案。
  3. 前端 React: 下載該 JSON 檔案,利用工具轉換成前端的 TypeScript SDK 套件。

這樣一來,即使後端從 Python 換成了 Go 語言,前後端依然能享受自動檢查欄位、不打翻代碼的完美工作流!


這是一個非常關鍵的架構抉擇!你看到的那些「 專門用來放 OpenAPI 檔案的獨立 Repo 」在業界也非常常見,這代表了兩種截然不同的開發哲學:程式碼優先( 後端自動生成 ) vs 設計優先( 手寫 OpenAPI 獨立管理 )

這兩種方法沒有絕對的好壞,而是取決於你們團隊的開發習慣與專案規模。以下為你詳細分析兩者的優缺點與適用場景:

方法一:後端自動產生( 程式碼優先 / Code-First )

就像前面提到的 FastAPI 或 Go Swag ,工程師直接寫後端程式碼,規格書由工具自動生出來。

優點:

  • 開發速度極快: 後端工程師不需要學 YAML 或 JSON 語法,只要專注在寫 Python 或 Go 即可。
  • 規格絕對不脫節: 因為文件是根據程式碼生出來的,所以程式碼只要能跑,規格書就一定是最新的,不會出現「 文件與實際程式不符 」的情況。

缺點:

  • 前端依然需要等待: 因為規格書是從後端程式碼生出來的,這意味著後端至少要把路由和資料結構( Struct )寫好並啟動伺服器,前端才能拿到 openapi.json 去生成自己的套件。雙方無法真正同時開工。
  • 容易包含過多後端實作細節: 有時候後端自動生成的結構會把資料庫的隱私欄位( 例如密碼、刪除時間等 )不小心也暴露在規格書中。

方法二:不使用自動產生,用獨立 Repo 管理( 設計優先 / Design-First )

這就是你看到的做法。前後端工程師在動工之前,先在一個獨立的 Git 倉庫( Repo )裡面,共同手寫一份 openapi.yaml 檔案。

優點:

  • 真正的並行開發( 同時開工 ): 當這份規格書在獨立 Repo 確定並版後,前端和後端可以同一天開始寫程式。前端直接用這份規格書生成 Mock( 假資料 )伺服器開始刻畫面;後端對著這份規格書開始寫邏輯,誰也不用等誰。
  • 極佳的合約精神: 任何 API 的修改都必須先在這個 Repo 提出 PR ( Pull Request ),經過前後端團隊審查、開會討論通過後才能合併。這能強迫團隊在動工前把規格想清楚。
  • 跨專案共享方便: 如果你的微服務架構有很多個後端、多個前端( 網頁、 App ),把規格獨立出來可以讓所有專案都去引用同一個來源。

缺點:

  • 維護成本高( 容易脫節 ): 如果後端工程師偷偷改了程式碼的欄位,卻忘記去那個獨立 Repo 更新 YAML 檔案,前端產生的套件就會出錯。這需要透過 CI/CD 流程加入自動化測試( 例如使用 Dredd 工具 )來強制檢查兩邊是否一致。
  • 初期進度緩慢: 專案初期會花很多時間在討論規格和寫 YAML 檔,對於習慣直接動手寫程式的工程師來說會覺得很綁手綁腳。

我該怎麼選擇?

推薦使用「 後端自動產生 」的場景:

  1. 時程非常緊迫的小型專案或個人專案。
  2. 後端工程師是主導者,且前端高度依賴後端提供的規格。
  3. 團隊人數少,口頭溝通非常順暢,不需要嚴格的合約審查。

推薦使用「 獨立 Repo 放 OpenAPI 」的場景:

  1. 中大型專案、前後端團隊各自獨立( 甚至前端和後端是不同外包廠商 )。
  2. 專案需要開發 Web 、 iOS 、 Android 多個端點,所有端點都需要看同一份規格。
  3. 團隊希望實施嚴格的程式碼審查( Code Review ),連 API 規格的改動都要留下紀錄與討論歷史。

不論選擇哪一種,重點都在於「 團隊的開發紀律 」。自動產生省心,獨立 Repo 規範嚴謹,選擇最適合你們目前團隊規模的做法才是最好的。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *