FastAPI 基礎概念(Fundamentals)
FastAPI 是什麼、與 Flask / Django 的比較
簡介
在現代 Web 開發中,API 已成為前後端分離、微服務與行動應用的核心橋樑。
FastAPI 是一套基於 Starlette(非同步網路框架)與 Pydantic(資料驗證)構建的 Python Web 框架,主打 高效能、自動產生 OpenAPI 文件 與 型別提示 的開發體驗。
對於剛接觸 Python Web 框架的開發者,了解 FastAPI 與傳統的 Flask、全功能的 Django 有何不同,有助於在專案初期選擇最適合的工具,減少未來的重構成本。
核心概念
1. 非同步(Async)與高效能
FastAPI 完全支援 async def,在處理 I/O 密集(如資料庫、外部 API)時,可讓單一執行緒同時服務多個請求。
相較之下,Flask 需要額外套件(如 gevent)才能達到類似效果;Django 在 3.1 之後才加入 async 支援,且生態系仍以同步為主。
重點:如果你的服務預期會有大量同時連線,選擇 FastAPI 能直接受惠於 Python 原生的非同步特性。
2. 型別提示 + 自動驗證
FastAPI 以 Python 型別註解 為基礎,自動產生請求參數的驗證與文件。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
tags: list[str] = [] # 預設值
@app.post("/items/")
async def create_item(item: Item):
"""
接收 JSON,FastAPI 會自動把它轉成 Item 實例,並驗證欄位類型與限制。
"""
return {"msg": f"已建立商品 {item.name},價格 {item.price}"}
上述程式碼只寫了型別,FastAPI 會在執行時檢查 name 必須是字串、price 必須是浮點數,若不符合會直接回傳 422 錯誤。
3. 自動產生 OpenAPI 與 Swagger UI
啟動 FastAPI 後,兩條預設路由即可看到 API 文件:
| 路由 | 功能 |
|---|---|
/docs |
Swagger UI(互動式測試) |
/redoc |
ReDoc(更完整的文件) |
只要在程式中加入路由與型別,文件即時更新,省去手寫或維護 Swagger 檔案的時間。
4. 輕量卻可擴充
FastAPI 本身只提供路由、請求/回應、依賴注入(Dependency Injection)等核心功能,其他需求(認證、資料庫、背景工作)都以 Starlette 中間件或第三方套件的方式加入。
| 功能 | Flask | Django | FastAPI |
|---|---|---|---|
| 認證/授權 | Flask‑Login、Flask‑JWT | Django Auth | fastapi‑users、OAuth2 |
| ORM | SQLAlchemy、Peewee | Django ORM | SQLModel、Tortoise‑ORM |
| 背景工作 | Celery(需自行整合) | Celery、Django‑Q | FastAPI + Celery / BackgroundTasks |
5. 依賴注入(Dependency Injection)
FastAPI 內建的依賴機制讓共用資源(如 DB 連線、設定)可在路由函式間「注入」而不必使用全域變數。
from fastapi import Depends, FastAPI
def get_db():
db = ... # 建立資料庫連線
try:
yield db
finally:
db.close()
@app.get("/users/{user_id}")
async def read_user(user_id: int, db = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
return user
這樣的設計不僅提升可測試性,也讓程式結構更清晰。
程式碼範例
以下提供 5 個實用範例,說明 FastAPI 常見操作與其優勢。
範例 1:最簡單的 Hello World
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def hello():
return {"message": "Hello, FastAPI!"}
只需要兩行程式,即可啟動一個可即時測試的 API。
範例 2:路由參數與驗證
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., title="商品 ID", gt=0),
q: str | None = Query(None, max_length=50)
):
"""
- `Path` 用於路徑參數驗證(必須大於 0)
- `Query` 用於查詢字串參數,可設定長度限制
"""
result = {"item_id": item_id}
if q:
result["q"] = q
return result
透過 Path 與 Query,開發者可以在宣告階段即完成參數驗證,減少手動檢查的程式碼。
範例 3:使用 Pydantic 建立複雜的請求模型
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=20)
email: EmailStr
password: str = Field(..., min_length=8)
@app.post("/users/")
async def create_user(user: UserCreate):
# 這裡可以直接使用 user.username、user.email ...
return {"msg": f"使用者 {user.username} 已建立"}
Pydantic 讓 資料模型、驗證、序列化 同時完成,且自動出現在 Swagger 文件中。
範例 4:依賴注入與資料庫(以 SQLModel 為例)
from fastapi import FastAPI, Depends
from sqlmodel import SQLModel, Session, create_engine, select
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, echo=True)
def get_session():
with Session(engine) as session:
yield session
app = FastAPI()
class Product(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
price: float
@app.on_event("startup")
def on_startup():
SQLModel.metadata.create_all(engine)
@app.post("/products/")
async def create_product(product: Product, session: Session = Depends(get_session)):
session.add(product)
session.commit()
session.refresh(product)
return product
透過 Depends(get_session),每次請求都會得到一個獨立的 DB session,避免共享狀態問題。
範例 5:背景任務(BackgroundTasks)
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def write_log(message: str):
with open("log.txt", "a") as f:
f.write(message + "\n")
@app.post("/notify/")
async def send_notification(email: str, background_tasks: BackgroundTasks):
# 假設此處呼叫外部郵件服務,時間較長
background_tasks.add_task(write_log, f"已發送通知給 {email}")
return {"msg": "通知已排程,請稍後查看信箱"}
使用 BackgroundTasks,可以把不需要即時回應的工作排到請求結束後執行,提升 API 的回應速度。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 混用同步與非同步程式碼 | 在 async 路由中直接呼叫阻塞的函式(如 time.sleep)會阻塞整個事件迴圈。 |
使用 await asyncio.sleep() 或將阻塞程式碼包在 run_in_threadpool 中。 |
| 未正確關閉資源 | DB session、檔案句柄等若未在結束時釋放,會造成記憶體泄漏。 | 利用依賴注入的 yield 方式,確保 finally 區塊執行清理。 |
| 過度依賴全域變數 | 全域的設定或連線在多執行緒環境下不安全。 | 使用 Depends 或 ContextVar 讓資源在請求範圍內管理。 |
| 忽略安全性 | 直接暴露 CRUD API,未加認證授權。 | 采用 OAuth2、JWT 或 fastapi-users 套件,配合 Depends 進行權限檢查。 |
| 文件未同步 | 手寫 Swagger 檔案或自行寫說明,易與實際程式碼不符。 | 依賴 FastAPI 自動產生的 OpenAPI,僅在需要特殊描述時才手動補充。 |
最佳實踐
- 型別註解必寫:讓 FastAPI 發揮自動驗證與文件生成的最大效益。
- 盡量使用 async:對 I/O 密集的路由(DB、外部 API)改寫為
async def,配合 async 驅動的套件(如httpx、databases)。 - 分層設計:將路由、服務層(business logic)與資料層分離,利用依賴注入傳遞服務物件。
- 測試驅動:FastAPI 支援
TestClient(基於requests)進行單元測試,建議在開發初期即加入測試。 - 使用容器化:將 FastAPI 包裝成 Docker 映像,搭配 Uvicorn/Gunicorn 的多工作者模式(
uvicorn workers)部署,可在生產環境取得更高吞吐量。
實際應用場景
| 場景 | 為何選 FastAPI |
|---|---|
| 微服務 API | 高併發、低延遲、自然支援 JSON Schema(OpenAPI),易於與其他服務(Kubernetes、Istio)整合。 |
| 資料科學模型部署 | 只需要幾行程式把模型封裝成 HTTP 端點,FastAPI 的自動文件讓前端或同事快速了解輸入輸出格式。 |
| 行動 App 後端 | 需要快速回應與 JWT 認證,FastAPI 可在同一程式碼庫內完成認證、授權與資料驗證。 |
| 即時訊息/長輪詢 | 配合 WebSocket(Starlette 原生支援),可在同一框架內同時提供 REST 與即時通訊。 |
| 內部管理系統 | 透過 fastapi-admin、fastapi-users 快速產生 CRUD 後台,減少開發時間。 |
總結
FastAPI 以 型別提示 + 非同步 為核心,結合 自動產生 OpenAPI、依賴注入 與 輕量可擴充 的設計哲學,為 Python 開發者提供了 比 Flask 更現代、比 Django 更靈活 的選擇。
- 若你的專案需要 高併發、快速迭代,或是 資料科學模型的即時服務,FastAPI 幾乎是首選。
- 若你已經在使用 Django 的完整 ORM、Admin 與模板系統,且需求以傳統網站為主,仍可考慮 Django。
- 若你只需要一個極簡的單頁 API,且不在乎非同步效能,Flask 仍是輕量的好選擇。
最終,選擇框架應該根據專案需求、團隊熟悉度與未來擴充性 來決定。無論是哪一條路,了解 FastAPI 的核心概念與最佳實踐,都能讓你在 Python Web 開發的旅程中走得更遠、更穩。