FastAPI 大型專案結構與 APIRouter
當你的 FastAPI 應用程式越來越大,將所有的程式碼都塞在一個 main.py 裡是不可行的。你需要將程式碼拆分成多個檔案,並組織成模組。
FastAPI 提供了 APIRouter,讓你能夠像主 app 一樣定義路由,最後再將它們「掛載」到主程式上。
推薦的目錄結構
以下是一個典型的大型 FastAPI 專案結構範例:
.
├── app
│ ├── __init__.py
│ ├── main.py # 程式入口
│ ├── dependencies.py # 共用的依賴項
│ └── routers # 路由模組
│ ├── __init__.py
│ ├── items.py # Items 相關的路徑操作
│ └── users.py # Users 相關的路徑操作
├── requirements.txt
└── .env
使用 APIRouter
讓我們來看看 app/routers/users.py 是如何寫的:
from fastapi import APIRouter
# 建立一個 Router
# prefix: 這底下所有路由的前綴路徑
# tags: 在 Swagger UI 中用來分類的標籤
# responses: 設定通用的錯誤回應
router = APIRouter(
prefix="/users",
tags=["users"],
responses={404: {"description": "Not found"}},
)
@router.get("/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
@router.get("/me")
async def read_user_me():
return {"username": "fakecurrentuser"}
@router.get("/{username}")
async def read_user(username: str):
return {"username": username}
注意到了嗎?這裡我們使用 @router.get(...) 而不是 @app.get(...)。
在主程式中掛載 Router
接下來,在 app/main.py 中,我們需要將這些 Router 匯入並加入到主 app 中。
from fastapi import FastAPI
from .routers import users, items
app = FastAPI()
# 使用 include_router 將定義好的 router 加入
app.include_router(users.router)
app.include_router(items.router)
@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}
include_router 的進階用法
include_router 不只是把路由加進來,它還能幫你做「批次化配置」。這在管理數十個甚至上百個路由時非常強大。
1. 網址前綴堆疊 (Prefix Stacking)
如果你在 APIRouter 中定義了 prefix="/users",而在 main.py 的 include_router 中又定義了 prefix="/api",則最終的網址會自動堆疊:
# app/routers/users.py
router = APIRouter(prefix="/info")
@router.get("/me") # 原始路徑是 /info/me
...
# app/main.py
app.include_router(users.router, prefix="/users")
最終造訪路徑會變成:http://localhost:8000/users/info/me。
2. 繼承標籤與錯誤回應
你可以透過 include_router 為特定的路徑分組統一加上 Swagger 標籤,這比在每個路由上個別加 tags 方便得多:
app.include_router(
items.router,
prefix="/items",
tags=["Items 管理系統"],
responses={418: {"description": "I'm a teapot"}},
)
3. 全域路由驗證 (Dependencies)
這是最具威力的用法:你可以直接為整個 Router 設定依賴項,常見於「特定區塊需要登入」的場景。
# 只有管理員可以存取 admin 路由下的所有路徑
app.include_router(
admin.router,
prefix="/admin",
dependencies=[Depends(verify_admin_access)]
)
為什麼要這樣設計?
- 關注點分離 (SoC):路由模組 (routers) 只需專注於業務邏輯的定義,而權限、前綴與分類則由主程式 (main.py) 統一控管。
- 減少重複 (DRY):你不需要在 50 個路由中重複寫「驗證 Token」的依賴項,只需在
include_router設定一次。 - 靈活性:如果你想更改整個
/items模組的 URL 前綴,只需動main.py的一行程式碼。
開發小撇步:避免循環引用 (Circular Imports)
在大型專案中,循環引用 是新手的惡夢(例如 A 匯入 B,B 又匯入 A)。
最佳實踐:
- 讓
app = FastAPI()留在main.py中。 routers下的各個檔案永遠不要匯入app或main.py。routers只匯入APIRouter來定義邏輯。- 最後由
main.py統一匯入routers並掛載。
這樣就能確保依賴鏈是單向的:main.py -> routers -> models/schemas。
總結
- 使用
APIRouter將路由邏輯拆分到不同的模組中。 - 使用
app.include_router()將子路由掛載到主應用程式。 - 善用
prefix,tags,dependencies的堆疊與繼承特性。 - 保持清晰的目錄結構對於專案的可維護性至關重要。