FastAPI 教學:自訂 OpenAPI Example(Swagger / ReDoc)
簡介
在使用 FastAPI 建立 API 時,除了撰寫正確的路由與邏輯外,文件化 也是不可或缺的工作。FastAPI 內建支援 OpenAPI(即 Swagger / ReDoc),會自動根據型別註解產生互動式文件,讓前端、測試或第三方開發者可以直接「點選」測試 API。
然而,預設產生的範例(example)往往過於簡單或不符合實務需求,導致文件看起來不具說服力,甚至讓使用者誤解欄位格式。自訂 Example 能讓文件更貼近真實資料、提升可讀性與測試效率,對於開發團隊與 API 消費者都非常重要。本文將一步步說明如何在 FastAPI 中設定自訂 Example,並提供多個實用範例、常見陷阱與最佳實踐,幫助你打造高品質的 API 文件。
核心概念
1. 為什麼需要自訂 Example?
- 提升可讀性:清楚示範欄位的資料型別與格式(如日期、enum、嵌套物件)。
- 加速開發測試:使用 Swagger UI 點擊「Try it out」時,預設值即為我們想要的測試資料。
- 降低錯誤率:讓 API 使用者不必自行猜測或查找說明文件,即可直接取得正確範例。
2. OpenAPI 中的 example 與 examples
example:單一範例值,適用於簡單欄位或整體回傳模型。examples:可提供多組範例,常用於說明不同情境(如成功 vs 錯誤)。
FastAPI 透過 Pydantic 的模型定義與 Path/Query/Body 參數的 example 參數,將這些資訊寫入 OpenAPI 文件。
3. 設定位置
自訂 Example 可以放在以下幾個層級:
| 層級 | 使用方式 | 典型情境 |
|---|---|---|
| Path / Query / Header | Path(..., example="123")、Query(..., example=10) |
單一參數的範例 |
| Request Body(Pydantic Model) | 在欄位 Field(..., example=…) 或模型 Config.schema_extra |
複雜物件、嵌套結構 |
| Response Model | response_model=MyModel + example / examples 於 Response |
回傳資料的範例 |
| 全局 OpenAPI | app.openapi_schema["components"]["schemas"] 手動編輯 |
需要一次性覆寫多個模型 |
程式碼範例
以下示範 5 個常見且實用的自訂 Example 實作方式。程式碼均以 Python(FastAPI)撰寫,使用 python 語法標記。
1️⃣ 基本 Path / Query 參數的 Example
from fastapi import FastAPI, Path, Query
app = FastAPI(title="自訂 Example 示範")
@app.get("/items/{item_id}")
def read_item(
item_id: int = Path(..., title="商品編號", example=42),
q: str | None = Query(None, title="搜尋關鍵字", example="fastapi")
):
"""
取得單一商品資訊。
- **item_id**: 商品編號,範例 42
- **q**: 可選的搜尋關鍵字,範例 "fastapi"
"""
return {"item_id": item_id, "q": q}
重點:
Path(..., example=42)直接把42放入 Swagger UI 的預設值,使用者只要點「Try it out」就會看到。
2️⃣ 使用 Field 為 Pydantic 欄位設定 Example
from pydantic import BaseModel, Field
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=20, example="alice")
password: str = Field(..., min_length=6, example="s3cr3t!")
email: str | None = Field(None, example="alice@example.com")
@app.post("/users", response_model=UserCreate)
def create_user(user: UserCreate):
"""
建立新使用者,Swagger UI 會自動帶入上述 Example。
"""
# 這裡省略實作,只回傳收到的資料
return user
說明:
Field(..., example=…)能為每個欄位提供獨立範例,對於 必填 與 可選 欄位皆適用。
3️⃣ 在模型層級一次設定多筆 Example(schema_extra)
class Product(BaseModel):
id: int = Field(..., example=1)
name: str = Field(..., example="Smartphone")
price: float = Field(..., gt=0, example=299.99)
tags: list[str] = Field(default_factory=list, example=["electronics", "mobile"])
class Config:
schema_extra = {
"example": {
"id": 101,
"name": "Wireless Earbuds",
"price": 79.5,
"tags": ["audio", "bluetooth"]
}
}
@app.get("/products/{product_id}", response_model=Product)
def get_product(product_id: int = Path(..., example=101)):
# 假設從資料庫撈資料,這裡直接回傳範例
return Product(**Product.Config.schema_extra["example"])
技巧:
schema_extra["example"]為模型層級的 單一 範例,適合在 回傳模型 中一次設定全部欄位。
4️⃣ 多組回傳範例(examples)與 HTTP 狀態碼
from fastapi.responses import JSONResponse
@app.get(
"/status/{code}",
responses={
200: {
"description": "成功回傳",
"content": {
"application/json": {
"example": {"message": "All good!"}
}
},
},
400: {
"description": "錯誤範例",
"content": {
"application/json": {
"examples": {
"missing": {
"summary": "缺少參數",
"value": {"detail": "Missing required query parameter"}
},
"invalid": {
"summary": "參數格式錯誤",
"value": {"detail": "Invalid value for parameter"}
},
}
}
},
},
},
)
def get_status(code: int = Path(..., example=200)):
if code == 200:
return JSONResponse(content={"message": "All good!"})
return JSONResponse(status_code=400, content={"detail": "Invalid request"})
說明:在
responses中使用examples可以一次提供多個錯誤回傳範例,Swagger UI 會在「Responses」區塊顯示所有情境,對測試非常友善。
5️⃣ 針對 ReDoc 顯示自訂 Example(使用 example 與 examples)
class Order(BaseModel):
order_id: str = Field(..., example="ORD-20231120-001")
amount: float = Field(..., gt=0, example=1500.75)
status: str = Field(..., example="pending")
items: list[dict] = Field(
...,
example=[
{"product_id": 101, "quantity": 2},
{"product_id": 205, "quantity": 1},
],
)
@app.post("/orders", response_model=Order, status_code=201)
def create_order(order: Order):
"""
建立訂單。ReDoc 會顯示完整的 JSON 範例,讓使用者一目了然。
"""
# 這裡僅回傳收到的資料
return order
要點:ReDoc 預設會顯示模型的
example(若有),若想要多組範例,可在 OpenAPIcomponents手動加入examples,不過對大多數情況而言,單一example已足夠。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 / Best Practice |
|---|---|---|
| 範例與實際驗證不一致 | 只在 example 裡寫假資料,卻忘了在 Field 加上 regex、gt 等驗證,導致文件看起來正確但實際請求失敗。 |
同時使用 example + 欄位驗證,確保範例符合模型限制。 |
忘記在 Path / Query 加 example |
Swagger UI 只會顯示空白欄位,使用者必須自行輸入,降低體驗。 | 統一規範:所有公開的參數皆必須提供 example(即使是 None 也可寫 example=None)。 |
過度冗長的 schema_extra |
把太多不必要的資料寫進 example,導致文件過大、載入緩慢。 |
只保留 關鍵欄位,其他欄位可省略或使用 ... 表示省略。 |
多組 examples 與 UI 不相容 |
某些舊版 Swagger UI 只支援單一 example,會忽略 examples。 |
若需兼容舊版,提供 單一 example 並在說明文字中列出其他情境。 |
| 範例包含敏感資訊 | 把真實 API 金鑰、密碼寫進 example,會在文件中公開。 |
使用 虛擬資料(如 ***、<your-token>),永遠不要寫入真實憑證。 |
推薦的寫作風格
- 一致性:所有模型與參數的
example都以相同的資料格式呈現(如日期使用 ISO 8601)。 - 可讀性:在
example中加入易懂的值(如"alice"、42),避免使用過於抽象的代碼。 - 分層說明:在模型說明文字(docstring)中補充 為什麼 需要這個欄位,讓文件更具教育意義。
- 版本管理:若 API 需變更範例,請同步更新
example與驗證規則,避免文件不同步。
實際應用場景
| 場景 | 為何需要自訂 Example | 實作概觀 |
|---|---|---|
| 前端開發者測試 UI | 前端需要快速知道 API 需要什麼樣的 JSON 結構。 | 在 UserCreate 模型使用 Field(..., example=…),讓 Swagger UI 直接生成測試資料。 |
| 第三方合作夥伴整合 | 合作夥伴常透過 Postman 匯入 OpenAPI,範例決定他們的測試腳本。 | 使用 responses 中的 examples 提供成功與錯誤的完整回傳範例。 |
| 自動化測試 (CI) | 測試腳本會根據 OpenAPI 產生測試資料。 | 在模型 Config.schema_extra 定義完整範例,測試框架自動抓取。 |
| API 文件外部發布 | 客戶或業務人員需要一份易讀的 API 手冊。 | 透過 ReDoc 部署,範例會直接呈現在文件中,提高說服力。 |
| 多語系或多環境 | 同一 API 在測試環境與正式環境的回傳結構略有差異。 | 使用 example 結合環境變數動態產生,或在 responses 中提供多組 examples。 |
總結
- 自訂 Example 是提升 FastAPI 文件可用性與專業度的關鍵技巧。
- 透過 Path / Query、Pydantic
Field、schema_extra以及responses,我們可以在不同層級提供單一或多組範例,讓 Swagger UI 與 ReDoc 更貼近實務需求。 - 注意 驗證一致性、避免敏感資訊、保持範例簡潔,並遵循 一致性與可讀性 的寫作規範,能有效減少文件與程式碼不同步的問題。
- 在實務上,無論是前端快速開發、第三方整合、或是自動化測試,適當的範例都能大幅縮短溝通成本,提升開發效率。
只要掌握本文的概念與範例,你就能在 FastAPI 專案中輕鬆加入高品質的自訂 Example,讓 API 文件不再只是機器產生的說明,而是真正 可讀、可測、可用 的開發資產。祝你寫出更好、更友善的 API!