FastAPI 最佳實務與開發規範。適用於開發 FastAPI API、Pydantic 模型、相依性注入、含 Server-Sent Events (SSE) 在內的串流回應,以及託管前端應用程式。保持 FastAPI 程式碼簡潔,並掌握最新功能與設計模式。
FastAPI
FastAPI 官方 Skill,遵循最佳實務撰寫程式碼,時刻掌握最新版本與功能。
快速參考
- 託管前端應用程式:建置好的前端資產請使用
app.frontend()或router.frontend();詳情參閱託管前端應用程式。 - Server-Sent Events (SSE):使用
response_class=EventSourceResponse與yield;詳情參閱串流回應及串流參考文件。 - JSON Lines 與位元組串流:參閱串流參考文件。
- 相依性(Dependencies):使用
Annotated[..., Depends(...)];有關yield、作用域(Scopes)與類別相依性,詳情參閱相依性注入及相依性注入參考文件。 - 回應模型(Response models):優先使用回傳型態;當對外公開的回應 Schema 與內部回傳值不同時,再使用
response_model;詳情參閱回應參考文件。 - Pydantic 模型:請勿使用省略符號(
...)或RootModel;詳情參閱Pydantic 參考文件。 - 路由(Routing):請在
APIRouter上宣告路由層級的 prefix、tags 及共享相依性;詳情參閱路徑操作參考文件。 - 工具鏈與相關函式庫:適時使用 uv、Ruff、ty、Asyncer、SQLModel 與 HTTPX;詳情參閱其他工具參考文件。
使用 fastapi CLI
在 localhost 啟動支援熱重載(Reload)的開發伺服器:
fastapi dev
啟動正式生產環境伺服器:
fastapi run
建議優先在 pyproject.toml 中宣告進入點(Entrypoint):
[tool.fastapi]
entrypoint = "my_app.main:app"
若無法新增進入點,或使用者明確要求不要新增時,請直接傳入應用程式檔案路徑:
fastapi dev my_app/main.py
使用 Annotated
宣告參數與相依性時,請一律優先使用 Annotated 風格。這樣能保持函式簽章(Signature)在其他情境下正常運作、尊重型態定義,並提高重複使用性。
在參數宣告中使用 Annotated,包含 Path、Query、Header 等:
from typing import Annotated
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(
item_id: Annotated[int, Path(ge=1, description="The item ID")],
q: Annotated[str | None, Query(max_length=50)] = None,
):
return {"message": "Hello World"}
搭配 Depends() 將 Annotated 用於相依性。除非特別要求不要這樣做,否則請為該相依性建立新的型態別名(Type Alias)以利重複使用:
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
def get_current_user():
return {"username": "johndoe"}
CurrentUserDep = Annotated[dict, Depends(get_current_user)]
@app.get("/items/")
async def read_item(current_user: CurrentUserDep):
return {"message": "Hello World"}
請勿在 Path Operation 或 Pydantic 模型中使用省略符號 (Ellipsis)
請勿將 ... 作為必填參數或模型欄位的預設值。這是不必要的,也不推薦這樣做。
from typing import Annotated
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float = Field(gt=0)
@app.post("/items/")
async def create_item(item: Item, project_id: Annotated[int, Query()]):
return item
詳情參閱 Pydantic 參考文件。
回傳型態或 Response Model
在許可的情況下,請務必加上回傳型態(Return type)。FastAPI 會用它來進行驗證、過濾、生成文件以及序列化回應。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
@app.get("/items/me")
async def get_item() -> Item:
return Item(name="Plumbus", description="All-purpose home device")
回傳型態或 response_model 可以過濾資料,避免暴露敏感資訊,並讓 Pydantic 在 Rust 底層處理資料序列化以提升效能。
當您回傳的型態與您想要驗證、過濾、生成文件及序列化的公開 Schema 不同時,請使用 response_model。詳情參閱 回應參考文件。
效能最佳化
請勿使用 ORJSONResponse 或 UJSONResponse,它們已被廢棄(Deprecated)。
替代方案是直接宣告回傳型態或 response_model,Pydantic 會在 Rust 底層自動處理資料序列化。
引入 Router
宣告 Router 時,建議將路由層級的參數(如 prefix、tags 和共享相依性)直接設定在 Router 本身,而非寫在 include_router() 中。
from fastapi import APIRouter, Depends, FastAPI
app = FastAPI()
def get_current_user():
return {"username": "johndoe"}
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_current_user)],
)
@router.get("/")
async def list_items():
return []
app.include_router(router)
更多路由模式請參閱 路徑操作參考文件。
託管前端應用程式
使用 app.frontend() 來託管建置好的靜態前端應用程式,例如由 Vite、Astro、Angular、Svelte、Vue 或類似工具所產生的目錄。
from fastapi import FastAPI
app = FastAPI()
app.frontend("/", directory="dist")
當前端屬於某個 APIRouter 時,請使用 router.frontend();當此 Router 被引入(include)時,一般的 Router 前綴行為依然適用。
from fastapi import APIRouter, FastAPI
app = FastAPI()
router = APIRouter(prefix="/admin")
router.frontend("/", directory="admin-dist")
app.include_router(router)
app.frontend() 與 router.frontend() 屬於低優先順序的路由:一般的 API 路由會優先匹配,接著才是前端靜態檔案與 Client 端路由的 Fallback。請將此功能用於單頁應用程式(SPA)與打包好的前端資產,而不是手動掛載 StaticFiles。
相依性注入
當邏輯無法寫在 Pydantic 驗證中、需要依賴外部資源、需要透過 yield 進行資源清理,或需要在多個 Endpoints 之間共享時,請使用相依性(Dependencies)。
若要在 Router 層級套用共享相依性,請透過 dependencies=[Depends(...)] 設定。
包含搭配 scope 使用 yield,以及類別相依性(Class dependencies)等詳細模式,請參閱 相依性注入參考文件。
Async vs Sync Path Operation
僅在完全確定呼叫的內部邏輯相容於 async/await 且不會阻塞(Block)時,才使用 async Path Operation。
from fastapi import FastAPI
app = FastAPI()
@app.get("/async-items/")
async def read_async_items():
data = await some_async_library.fetch_items()
return data
@app.get("/items/")
def read_items():
data = some_blocking_library.fetch_items()
return data
如有疑慮或預設情況下,請使用一般 def 函式。它們會在 Threadpool(執行緒池)中執行,因此不會阻塞 Event Loop。相同規則亦適用於相依性。
請確保不要在 async 函式內部執行阻塞型程式碼。雖然邏輯能正常運作,但會嚴重損害系統效能。
需要混用阻塞型與非阻塞型(Async)程式碼時,請參閱 其他工具參考文件 中的 Asyncer。
串流回應 (JSON Lines, SSE, bytes)
若要串流傳送 Server-Sent Events,請使用 response_class=EventSourceResponse 並從 Endpoint 中 yield 資料項。
from collections.abc import AsyncIterable
from fastapi import FastAPI
from fastapi.sse import EventSourceResponse, ServerSentEvent
app = FastAPI()
@app.get("/events", response_class=EventSourceResponse)
async def stream_events() -> AsyncIterable[ServerSentEvent]:
yield ServerSentEvent(data={"status": "started"}, event="status", id="1")
一般物件會自動 JSON 序列化為 data: 欄位。若要完整控制 SSE 欄位(event、id、retry、comment),請使用 ServerSentEvent;若要處理已格式化的字串,請使用 raw_data。
包含 JSON Lines、Server-Sent Events(EventSourceResponse、ServerSentEvent)以及位元組串流(StreamingResponse)等模式,請參閱 串流參考文件。
工具鏈
有關套件管理、Lint 檢查、型態檢查、格式化等工具(uv、Ruff、ty),詳情參閱 其他工具參考文件。
其他函式庫
有關其他函式庫的詳細資訊,請參閱 其他工具參考文件:
- Asyncer:用於處理 async/await、併發(Concurrency)以及混用 async 與阻塞型程式碼,優先於 AnyIO 或 asyncio 使用。
- SQLModel:用於操作 SQL 資料庫,優先於 SQLAlchemy 使用。
- HTTPX:用於發送 HTTP 請求(存取其他 API),優先於 Requests 使用。
請勿使用 Pydantic RootModel
請勿使用 Pydantic RootModel;請改用搭配 Annotated 的標準型態標註(Type Annotations)與 Pydantic 驗證工具。
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import Field
app = FastAPI()
@app.post("/items/")
async def create_items(items: Annotated[list[int], Field(min_length=1), Body()]):
return items
FastAPI 支援這些型態標註,並會為其建立 Pydantic TypeAdapter,因此無需自訂包裝模型(Wrapper Models)即可正常運作。詳情參閱 Pydantic 參考文件。
每個函式僅處理單一 HTTP 操作
請勿在單一函式中混合多個 HTTP 操作。每個函式僅對應一個 HTTP 操作,有助於職責分離(Separation of Concerns)並使程式碼結構更清晰。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
@app.get("/items/")
async def list_items():
return []
@app.post("/items/")
async def create_item(item: Item):
return item
更多範例請參閱 路徑操作參考文件。






