本文 AI 產出,尚未審核
FastAPI 教學 – 應用結構與啟動方式:Application Factory Pattern
簡介
在大型或可擴充的 FastAPI 專案中,直接在 main.py 中寫死所有路由、設定與中介軟體,往往會造成維護成本高、測試困難的問題。
Application Factory Pattern(應用工廠模式)提供了一種「延遲建立」FastAPI 物件的方式,讓我們可以在不同情境(開發、測試、部署)下,使用相同的程式碼基礎,卻得到不同的設定與相依性。
透過工廠函式,我們可以:
- 分離設定與路由,讓程式碼結構更清晰。
- 在測試時輕鬆產生獨立的 app 實例,避免測試互相干擾。
- 支援多種啟動方式(如
uvicorn main:app、Docker、Gunicorn),只要呼叫同一個工廠即可。
以下將一步一步說明這個模式的核心概念與實作細節,並提供實用範例、常見陷阱與最佳實踐,幫助你在專案中快速導入。
核心概念
1. 什麼是 Application Factory
Application Factory 本質上是一個返回 FastAPI 實例的函式。
它不會在模組載入時直接建立 FastAPI(),而是等到程式需要時才呼叫,讓我們可以在建立過程中注入設定、註冊路由、加入中介軟體等。
def create_app(config_object: str = "app.config.DevConfig") -> FastAPI:
app = FastAPI()
# 讀取設定
app.state.config = import_string(config_object)()
# 註冊路由、事件、middleware...
return app
重點:工廠函式接受參數(如設定檔路徑),使同一套程式碼可以在不同環境下產生不同的
app。
2. 專案目錄建議
myproject/
├─ app/
│ ├─ __init__.py # 只放 create_app
│ ├─ config.py # 各環境設定類別
│ ├─ api/
│ │ ├─ __init__.py
│ │ └─ v1.py # 路由模組
│ ├─ core/
│ │ ├─ __init__.py
│ │ └─ middleware.py # 中介軟體
│ └─ models/ # ORM / Pydantic
├─ tests/
│ └─ test_api.py
├─ main.py # 只負責呼叫 create_app
└─ requirements.txt
3. 實作步驟
3.1 建立設定類別
# app/config.py
class BaseConfig:
TITLE = "My FastAPI Service"
DEBUG = False
class DevConfig(BaseConfig):
DEBUG = True
LOG_LEVEL = "debug"
class ProdConfig(BaseConfig):
LOG_LEVEL = "info"
3.2 撰寫工廠函式
# app/__init__.py
from fastapi import FastAPI
from importlib import import_module
from .config import BaseConfig
def create_app(config_class: str = "app.config.DevConfig") -> FastAPI:
"""Application factory for FastAPI."""
# 動態匯入設定類別
module_path, class_name = config_class.rsplit(".", 1)
config = getattr(import_module(module_path), class_name)()
app = FastAPI(
title=config.TITLE,
debug=config.DEBUG,
)
app.state.config = config # 讓其他模組可以存取設定
# 1️⃣ 註冊中介軟體
from .core.middleware import add_middlewares
add_middlewares(app)
# 2️⃣ 註冊路由
from .api import v1
app.include_router(v1.router, prefix="/api/v1")
# 3️⃣ 設定啟動與關閉事件
@app.on_event("startup")
async def on_startup():
# 例如建立資料庫連線
pass
@app.on_event("shutdown")
async def on_shutdown():
# 關閉連線
pass
return app
3.3 路由模組化
# app/api/v1.py
from fastapi import APIRouter, Depends
router = APIRouter()
def get_current_user():
# 假設的依賴項
return {"username": "demo"}
@router.get("/ping")
async def ping():
"""最簡單的健康檢查端點"""
return {"msg": "pong"}
@router.get("/users/me")
async def read_me(user: dict = Depends(get_current_user)):
"""示範使用 Depends 注入"""
return {"user": user}
3.4 中介軟體範例
# app/core/middleware.py
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
def add_middlewares(app: FastAPI) -> None:
"""在 app 中加入跨域與自訂中介軟體"""
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 你可以在此加入日誌、驗證等自訂中介軟體
3.5 入口檔案(main.py)
# main.py
import uvicorn
from app import create_app
app = create_app("app.config.DevConfig") # 依需求切換設定
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
4. 測試時的應用
使用 TestClient,只要呼叫工廠即可得到一個 乾淨、獨立 的 app 實例。
# tests/test_api.py
from fastapi.testclient import TestClient
from app import create_app
def test_ping():
app = create_app("app.config.DevConfig")
client = TestClient(app)
response = client.get("/api/v1/ping")
assert response.status_code == 200
assert response.json() == {"msg": "pong"}
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
在模組層級直接建立 FastAPI() |
會導致全域單例,測試時無法重置狀態。 | 使用 factory,僅在需要時呼叫 create_app()。 |
| 設定檔硬寫在程式碼內 | 難以在不同環境切換,且容易把機密資訊寫進版本控制。 | 把設定抽成類別或 pydantic.BaseSettings,並支援環境變數。 |
| 路由與中介軟體散落在多個檔案卻未統一註冊 | 可能遺漏某些路由,導致 404。 | 在工廠內統一 include_router、add_middleware,確保所有模組都被載入。 |
| 忘記在測試結束後關閉資源 | 例如資料庫連線未關閉,會佔用端口。 | 使用 @app.on_event("shutdown") 或 pytest 的 fixture 來清理。 |
在 __init__.py 中執行副作用 |
會在 import 時執行不必要的程式,影響啟動速度。 | 保持 __init__.py 輕量,僅匯出 create_app。 |
最佳實踐小結
- 工廠函式接受設定類別路徑,讓 CLI、Dockerfile、CI 都能簡單切換。
- 將路由、模型、服務層分離,每個子模組只負責單一職責。
- 使用
app.state或ContextVar保存全域資源,避免使用全域變數。 - 在測試中使用
TestClient,每個測試函式都呼叫create_app(),確保隔離。 - 加入型別提示與 docstring,提升 IDE 補全與自動文件生成的品質。
實際應用場景
| 場景 | 為何需要 Application Factory |
|---|---|
| 微服務叢集 | 每個服務都有不同的環境變數與資料庫,工廠讓部署腳本只改參數即可。 |
| CI/CD 自動化測試 | 測試 pipeline 會多次啟動 FastAPI,使用工廠可以快速產生乾淨的測試環境。 |
| 多版本 API | 透過 include_router 並在工廠內根據設定載入不同版本的路由,維護成本降低。 |
| 動態插件 | 需要在運行時根據外部設定載入額外的路由或中介軟體,工廠提供了「組裝」的彈性。 |
| 本機開發與容器化 | 開發時使用 DevConfig(開啟 reload、debug),上線時只換成 ProdConfig,程式碼不需要變動。 |
總結
Application Factory Pattern 是構建可維護、可測試、可擴充 FastAPI 專案的基礎建築。透過延遲建立 FastAPI 實例,我們可以:
- 清晰分離設定、路由與中介軟體。
- 在不同環境間輕鬆切換(開發 / 測試 / 生產)。
- 提升測試效率與可靠性,每次測試都有獨立的 app。
只要遵循本文的目錄結構、工廠實作方式與最佳實踐,你的 FastAPI 專案將具備良好的可讀性與可維護性,未來再加入新功能或部署到雲端都會更加順暢。快把這套模式套用到自己的專案中,體驗 乾淨、彈性、可測 的開發流程吧!