本文 AI 產出,尚未審核

FastAPI 路由(Routing)

主題:路由前綴(APIRouter prefix)


簡介

在開發大型 API 時,路由的組織與維護往往是最容易變得雜亂的部份。若每個 endpoint 都直接寫在 main.py,隨著功能增多,檔案會迅速變得難以閱讀、測試與重構。FastAPI 為此提供了 APIRouter,讓我們可以把相關的路由切分成模組,並透過 prefix 給予統一的 URL 前綴,讓 API 結構更清晰、路徑管理更一致。

本文將說明 APIRouter prefix 的概念、使用方式與實務上的好處,並提供多個可直接套用的範例,協助你在專案中快速導入這項功能。


核心概念

1️⃣ 為什麼需要 prefix

  • 統一命名空間:將同一類別的資源(例如使用者、商品)集中在同一段路徑下,如 /users/*/products/*
  • 降低重複程式碼:不必在每個路由上手動寫相同的字串,減少打錯或遺漏的機會。
  • 方便版本管理:只要在根路由加上版本前綴(/v1/v2),所有子路由自動跟隨,升級時更省事。

2️⃣ 基本語法

from fastapi import FastAPI, APIRouter

app = FastAPI()

router = APIRouter(
    prefix="/users",          # <--- 這裡設定前綴
    tags=["users"],           # 用於自動產生的 API 文件分組
    responses={404: {"description": "Not found"}}
)

@router.get("/")
async def list_users():
    return [{"id": 1, "name": "Alice"}]

@router.get("/{user_id}")
async def get_user(user_id: int):
    return {"id": user_id, "name": "Bob"}

app.include_router(router)
  • prefix 只會在 include 時套用,原始路由定義仍保持簡潔。
  • 若在 router 中再加上子路由(如 @router.get("/profile")),最終路徑會是 /users/profile

3️⃣ 多層路由前綴

FastAPI 允許 巢狀 APIRouter,讓你可以同時設定全局前綴與模組前綴:

api_v1 = APIRouter(prefix="/v1")
users_router = APIRouter(prefix="/users")

@users_router.get("/")
async def list_users():
    ...

api_v1.include_router(users_router)   # 完整路徑 => /v1/users/
app.include_router(api_v1)

這樣的結構在 微服務多版本 API 時特別有用。

4️⃣ 前綴與依賴(Dependencies)的結合

APIRouter 也支援 依賴注入,可以在前綴層級設定共用的依賴,所有子路由自動取得:

from fastapi import Depends, Header

def verify_token(x_token: str = Header(...)):
    if x_token != "secret-token":
        raise HTTPException(status_code=401, detail="Invalid token")
    return x_token

secure_router = APIRouter(
    prefix="/admin",
    dependencies=[Depends(verify_token)]
)

@secure_router.get("/stats")
async def get_stats():
    return {"users": 123, "orders": 456}

只要把 secure_router 加入 app,所有 /admin/* 路徑都會先執行 verify_token

5️⃣ 範例彙總:從零建構一個分層 API

以下示範一個 完整的專案結構,包含三個子路由(usersitemsorders),每個子路由都有自己的 prefixtags,最外層再加上版本前綴:

project/
│
├─ main.py
├─ routers/
│   ├─ __init__.py
│   ├─ users.py
│   ├─ items.py
│   └─ orders.py

main.py

from fastapi import FastAPI
from routers import users, items, orders

app = FastAPI(title="Shop API")

api_v1 = APIRouter(prefix="/v1")
api_v1.include_router(users.router)
api_v1.include_router(items.router)
api_v1.include_router(orders.router)

app.include_router(api_v1)

routers/users.py

from fastapi import APIRouter

router = APIRouter(
    prefix="/users",
    tags=["users"]
)

@router.get("/", summary="取得所有使用者")
async def list_users():
    return [{"id": 1, "name": "Alice"}]

@router.get("/{user_id}", summary="取得單一使用者")
async def get_user(user_id: int):
    return {"id": user_id, "name": "Bob"}

routers/items.py

from fastapi import APIRouter, HTTPException

router = APIRouter(
    prefix="/items",
    tags=["items"]
)

items_db = {1: {"name": "Laptop"}, 2: {"name": "Phone"}}

@router.get("/", summary="列出所有商品")
async def list_items():
    return items_db

@router.get("/{item_id}", summary="取得單一商品")
async def get_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return items_db[item_id]

routers/orders.py

from fastapi import APIRouter, Depends

router = APIRouter(
    prefix="/orders",
    tags=["orders"]
)

def get_current_user():
    # 假設從 token 取得使用者資訊
    return {"id": 1, "name": "Alice"}

@router.post("/", summary="建立新訂單")
async def create_order(order: dict, user: dict = Depends(get_current_user)):
    # 實作略...
    return {"msg": "Order created", "user": user}

重點:只要在 main.pyapi_v1 包含進 app,所有路徑自動變成 /v1/users/*/v1/items/*/v1/orders/*,而且每個子路由的程式碼都保持 模組化、易於測試


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記在 include_router 時保留前綴 若在子路由內再次設定 prefix,但在 include_router 時未傳入 prefix,會導致路徑不如預期。 統一在 APIRouter 建構子 設定 prefix,不要在 include_router 再次加入相同前綴。
前綴與路由參數衝突 例如 prefix="/users/{user_id}" 再寫 @router.get("/{item_id}"),會產生路徑 /users/{user_id}/{item_id},易混淆。 避免在前綴中使用參數,除非確實需要層級資源(如 /users/{user_id}/items),此時應清楚文件化。
過度嵌套 多層 APIRouter 嵌套雖然靈活,但過深會使除錯變難。 保持兩層以內(版本 + 功能),若需要更細分,可考慮在功能路由內使用 子路徑 而非再套 APIRouter
依賴被覆寫 子路由的 dependencies覆寫 父路由的同類型依賴,若不小心會失去全域驗證。 明確使用 dependencies= 參數,或在子路由內 Depends 時呼叫 parent_dependency
Swagger 文檔分組不一致 tags 未統一設定,Swagger 會把同一模組的路由分散。 在每個 APIRouter 建構時 指定 tags,確保 UI 上的分組清晰。

最佳實踐

  1. 模組化:每個業務領域(User、Item、Order)各自放在 routers/ 資料夾,保持 main.py 只做組合工作。
  2. 統一前綴:將版本號、語系、租戶等公共資訊放在最外層 APIRouter,子路由只負責功能前綴。
  3. 使用 tagssummary:讓自動產生的 OpenAPI 文件更易讀。
  4. 測試路由:利用 TestClient 測試每個 APIRouter,確保前綴不會因變更而斷裂。
  5. 文件化:在程式碼註解或 README 中說明每個前綴的意義,避免新加入的開發者產生疑惑。

實際應用場景

場景 前綴使用方式 為什麼適合
多租戶 SaaS 平台 APIRouter(prefix="/{tenant_id}") + 各功能子路由 讓每個租戶的 API 完全隔離,只需在最外層加入租戶參數即可。
API 版本管理 APIRouter(prefix="/v1")APIRouter(prefix="/v2") 新增功能或變更行為時,只需新增新版本的路由,舊版仍可繼續服務。
微服務網關 在 API Gateway 中使用 APIRouter(prefix="/auth")APIRouter(prefix="/payment") 把不同微服務的入口統一在同一 FastAPI 應用內,減少部署成本。
手機與 Web 前端分流 APIRouter(prefix="/mobile")APIRouter(prefix="/web") 針對不同客戶端提供客製化的回傳結構或驗證方式。
功能開關(Feature Flag) APIRouter(prefix="/beta") 只在測試環境 include 讓測試版功能僅在特定環境可見,正式環境不會暴露給使用者。

總結

  • APIRouter prefix 是 FastAPI 用來 統一路徑命名、簡化程式碼、支援版本與多租戶 的強大工具。
  • 透過 模組化、層級前綴,我們可以在保持程式結構清晰的同時,快速擴充功能。
  • 注意 避免前綴參數衝突、過度嵌套,並善用 tagssummary 讓自動產生的文件更具可讀性。
  • 在實務專案中,將 版本、租戶、功能 分別放在不同層級的 APIRouter,即可同時滿足 可維護性彈性擴充 的需求。

掌握了路由前綴的使用,你的 FastAPI 專案將從「雜亂」變成「條理分明」,開發與維護都會變得更輕鬆。祝你在打造高效 API 的路上一路順風 🚀。