fastapi

fastapi

热门

FastAPI 最佳实践与编码规范。适用于开发 FastAPI API、定义 Pydantic 模型、处理依赖注入、实现流式响应(含 SSE 服务端推送)以及托管前端应用。帮助你保持 FastAPI 代码整洁优雅,并紧跟最新的特性与设计模式。

10万Star
9717Fork
更新于 2026/7/29
SKILL.md
只读
名称
fastapi
描述

FastAPI 最佳实践与编码规范。适用于开发 FastAPI API、定义 Pydantic 模型、处理依赖注入、实现流式响应(含 SSE 服务端推送)以及托管前端应用。帮助你保持 FastAPI 代码整洁优雅,并紧跟最新的特性与设计模式。

FastAPI

FastAPI 官方 Skill,旨在遵循最佳实践编写代码,并紧跟新版本与最新特性。

快速参考

  • 托管前端应用:对于构建好的前端静态资源,使用 app.frontend()router.frontend();详见托管前端应用
  • Server-Sent Events (SSE):使用 response_class=EventSourceResponse 并配合 yield 语句;详见流式传输流式传输参考指南
  • JSON Lines 和字节流传输:详见流式传输参考指南
  • 依赖项:推荐使用 Annotated[..., Depends(...)] 语法;关于 yield、作用域与类依赖,详见依赖注入依赖注入参考指南
  • 响应模型:优先使用返回类型注解;仅当公开的响应 Schema 与内部返回值不一致时才使用 response_model;详见响应参考指南
  • Pydantic 模型:避免使用省略号 ...RootModel;详见Pydantic 参考指南
  • 路由控制:直接在 APIRouter 上声明路由级 prefixtags 和共享依赖项;详见路径操作参考指南
  • 工具链与相关库:适时选择 uv、Ruff、ty、Asyncer、SQLModel 和 HTTPX 等现代化工具;详见其它工具参考指南

使用 fastapi CLI

使用 reload 模式在本地启动开发服务器:

fastapi dev

启动生产环境服务器:

fastapi run

推荐在 pyproject.toml 中声明应用入口点:

[tool.fastapi]
entrypoint = "my_app.main:app"

当无法添加入口点或用户显式要求不添加时,直接传递应用文件路径:

fastapi dev my_app/main.py

优先使用 Annotated

在参数和依赖声明中,始终优先使用 Annotated 风格。这种写法能确保函数签名在其他上下文中仍然兼容、保留明确的类型提示,并大幅提升代码复用性。

在声明包含 PathQueryHeader 等参数时使用 Annotated

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。除非明确要求不使用,否则建议为该依赖新建一个类型别名,以便复用:

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"}

不要在路径操作或 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 参考指南

返回类型与响应模型

只要条件允许,务必加上函数返回类型注解。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")

返回类型注解或响应模型能够过滤敏感数据,防止意外暴露,同时允许 Pydantic 在 Rust 底层加速序列化,从而大幅提升性能。

仅当函数内部返回的数据类型与希望对外展示、校验、过滤和生成文档的 Schema 不一致时,才需要使用 response_model。详见响应参考指南

性能优化

不要再使用 ORJSONResponseUJSONResponse,它们已被废弃。

正确的做法是声明返回类型或响应模型,Pydantic 会自动利用 Rust 底层为你处理高效的数据序列化。

包含路由

声明路由时,建议将 prefixtags 以及共享依赖项等路由层级参数直接写在 APIRouter 实例化处,而不是传给 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 时,标准的前缀(prefix)行为依然生效。

from fastapi import APIRouter, FastAPI

app = FastAPI()
router = APIRouter(prefix="/admin")

router.frontend("/", directory="admin-dist")
app.include_router(router)

app.frontend()router.frontend() 属于低优先级路由:FastAPI 会优先匹配常规的 API 路由,未命中时才会 fallback 到静态前端文件和单页应用客户端路由。对于单页应用(SPA)和打包好的前端资源,应优先使用该方式,而不是手动挂载 StaticFiles

依赖注入

当某种逻辑无法直接写在 Pydantic 校验中、依赖外部资源、需要用 yield 进行清理收尾,或者被多个 Endpoint 共享时,请使用依赖注入。

对于共享依赖项,建议通过 dependencies=[Depends(...)] 应用于路由器(router)层级。

关于包含 yield 和作用域(scope)的用法以及类依赖项的具体细节,详见依赖注入参考指南

异步 (Async) 与同步 (Sync) 路径操作

仅当明确确认内部调用的逻辑完全兼容 async/await 且绝对不会阻塞线程时,才使用 async 路径操作。

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 同步函数。FastAPI 会自动将它们放入线程池运行,不会阻塞事件循环(Event Loop)。这一规则同样适用于依赖项。

切记不要在 async 函数内部运行阻塞代码。虽然程序依然可以运行,但会严重损害整体性能。

如果需要混合使用阻塞代码与异步代码,请参阅其它工具参考指南中的 Asyncer。

流式传输(JSON Lines、SSE、字节流)

如需实现 Server-Sent Events (SSE) 服务端推送,请使用 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")

普通的 Python 对象会被自动序列化为 JSON,并填入 data: 字段中。若需要完全掌控 SSE 字段(如 eventidretrycomment),请使用 ServerSentEvent 对象;如需传输预先格式化好的字符串,可使用 raw_data

关于 JSON Lines、Server-Sent Events(EventSourceResponseServerSentEvent)以及字节流传输(StreamingResponse)的具体范式,详见流式传输参考指南

工具链

关于包管理、代码 Linting、类型检查、格式化工具(如 uv、Ruff、ty 等)的具体用法,详见其它工具参考指南

其它推荐库

关于其它第三方库的详细介绍,请参阅其它工具参考指南

  • Asyncer:用于处理 async/await、并发控制以及混合异步与阻塞代码。相较于 AnyIO 或 asyncio,优先推荐使用 Asyncer。
  • SQLModel:用于操作 SQL 数据库。相较于 SQLAlchemy,优先推荐使用 SQLModel。
  • HTTPX:用于发起 HTTP 请求(调用外部 API)。相较于 Requests,优先推荐使用 HTTPX。

不要使用 Pydantic RootModel

不要使用 Pydantic 的 RootModel;相反,应该使用标准的类型注解配合 Annotated 以及 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 Model)。详见Pydantic 参考指南

每个函数仅对应一个 HTTP 操作

不要在一个函数中混合处理多种 HTTP 操作。遵循“一个函数处理一个 HTTP 操作”的原则,有助于职责分离并使代码结构更加清晰。

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

更多示例详见路径操作参考指南