09 · 服务端语言上手:FastAPI + Python 服务端
你已经会前端,现在要写第一个后端服务。本篇用 FastAPI 把"从 Hello World 到可部署服务"一次讲通,全程用前端心智做对照。
速览卡
| 项 | 内容 |
|---|---|
| 一句话定位 | 前端转后端的第一个动手篇:FastAPI 的最小完整链路 + 与前端心智的差异点 |
| 核心内容 | FastAPI 为什么适合前端转 AI 全栈、最小服务、请求校验(Pydantic)、依赖注入、异步模型、配置与部署最小步骤 |
| 读完能回答 | FastAPI 和 Express 有什么本质区别?Python 的 async 和 JS 的事件循环一样吗?Pydantic 是什么? |
| 相关板块 | C00 · 学习全景 | E01 · Python 基础 | C02 · API 设计 |
一、为什么是 FastAPI
| 维度 | FastAPI | 说明 |
|---|---|---|
| 生态 | 衔接 AI 全家桶 | LangChain、OpenAI SDK、向量库全是 Python 优先 |
| 类型友好 | Pydantic 校验 = 运行时的 TS 类型 | 前端最容易上手的 Python 后端框架 |
| 异步 | 原生 async/await | 心智模型和 JS 几乎一致 |
| 自带文档 | OpenAPI + Swagger UI | 写接口自带可点页面,调试成本低 |
| 性能 | 基于 Starlette/Uvicorn | 非性能瓶颈场景足够 |
一句话:你做 AI 全栈,后端主线就选 Python + FastAPI。C01 已经论证过"AI 生态在 Python 一侧",这里不再重复。
二、最小可运行服务
2.1 初始化
mkdir my-api && cd my-api
python -m venv .venv # 类似 npm 的局部 node_modules
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install fastapi "uvicorn[standard]"venv 类比 npm:
.venv/是项目的独立 Python 环境,装包不污染全局。依赖清单用pip freeze > requirements.txt(对应package.json+ lockfile)。
2.2 Hello World
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "hello from backend"}uvicorn main:app --reload --port 8000访问 http://localhost:8000/docs,你会看到自动生成的 Swagger 文档——这是 FastAPI 与 Express 最直观的差异:类型注解即文档。
三、请求校验:Pydantic 就是"运行时的 TypeScript"
3.1 定义一个模型
from typing import Optional
from pydantic import BaseModel, Field
class ItemCreate(BaseModel):
name: str = Field(min_length=1, max_length=50)
price: float = Field(gt=0)
tag: Optional[str] = None3.2 在路由中使用
from fastapi import FastAPI
app = FastAPI()
@app.post("/items")
def create_item(item: ItemCreate):
# 请求体会被自动校验;不合法直接返回 422
return {"id": 1, **item.model_dump()}与 TS 的关键差异:TS 的类型在编译期检查、运行时空空如也;Pydantic 在运行时校验——非法请求会被挡在业务代码之外,返回标准 422。这就是"运行时的 TypeScript"的含义。
⚠️
model_dump()返回 dict(旧版dict()),不要用item.json()存库,那会返回字符串。
四、依赖注入:从"手动取"到"声明我要"
前端里你会手动 new Service() 或从容器拿实例。FastAPI 用依赖注入自动装配:
from fastapi import Depends
def get_db():
db = DBSession()
try:
yield db # 请求结束自动关闭
finally:
db.close()
@app.get("/items/{item_id}")
def read_item(item_id: int, db=Depends(get_db)):
return db.query(item_id)类比:
Depends(get_db)像前端框架的依赖容器——你不写"创建连接、用完关闭",框架按声明帮你做。yield语法用于保证请求结束后的清理。
五、异步模型:像 JS,但有一条铁律
| 概念 | JS(Node) | Python(asyncio) |
|---|---|---|
| 单线程事件循环 | ✅ | ✅(一个线程跑事件循环) |
| 协程关键字 | async/await | async/await |
| 真正并行 | 靠 worker 线程/进程 | 靠多进程 / 多事件循环 |
| 阻塞陷阱 | 同步阻塞调用卡事件循环 | 同理:time.sleep 会卡住整个事件循环 |
import asyncio
@app.get("/slow")
async def slow():
await asyncio.sleep(1) # ✅ 正确:让出事件循环
return {"ok": True}import time
@app.get("/wrong")
async def wrong():
time.sleep(1) # ❌ 阻塞整个事件循环,所有请求都卡住
return {"ok": True}铁律:在
async函数里绝不能调同步阻塞库(time.sleep、requests.get)。要么用异步版本(httpx.AsyncClient、aiosqlite),要么把函数声明成普通def(FastAPI 会丢给线程池)。
六、配置与环境变量
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "My API"
database_url: str
openai_api_key: str = ""
model_config = {"env_file": ".env"}
settings = Settings()
.env文件对应前端的.env用法;密钥绝不能硬编码进代码。BaseSettings会自动读环境变量和.env。
七、部署最小步骤
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]docker build -t my-api .
docker run -p 8000:8000 --env-file .env my-api部署细节(多环境、CI/CD、反向代理)见 C17 · 部署与 CI/CD。
自检清单
- [ ] 能写出一个带请求体校验的 POST 接口,并知道 422 是谁返回的
- [ ] 能用
Depends写依赖注入,并解释yield的作用 - [ ] 能说清 Python async 与 JS 事件循环的相同点和"阻塞陷阱"这条铁律
- [ ] 能用
BaseSettings读环境变量,不硬编码密钥 - [ ] 能写 Dockerfile 并本地跑起容器
常见误区
- 误区:Python 类型注解会在运行时校验。 不会。运行时校验靠 Pydantic 显式声明,普通注解只是给人和 IDE 看。
- 误区:
async def就自动并发。 不是。里面的同步阻塞调用照样卡死事件循环。 - 误区:
.venv和node_modules一样可以删了重建。 可以,但要记得有requirements.txt才能恢复,先pip freeze。 - 误区:FastAPI 只适合小项目。 它一样能做中大型服务,只是生态和组织方式要自己搭(见 C05)。
动手练习
练习 A:Todo API
写一个内存版 Todo API:GET /todos、POST /todos(带校验)、DELETE /todos/{id}。在 /docs 里逐个调试。
练习 B:加一个异步慢接口
写一个 GET /slow(用 asyncio.sleep)和 GET /wrong(用 time.sleep),用两个并发请求对比差异,确认你理解了阻塞陷阱。
练习 C:容器化
给 Todo API 写 Dockerfile,本地 docker build + docker run 跑通。
相关文档
本区
- 00 · 后端学习全景 —— 学习路径总索引
- 01 · 运行时选型 —— 为什么主线选 Python
- 02 · API 设计 —— 接口约定与错误体
- 10 · 数据建模与数据访问 —— 下一个要学的:连数据库
- 17 · 部署与 CI/CD —— 从本机到云端
其他域
- E01 · Python 语言基础与 TS 对照 —— Python 语法与陷阱
- 23 · Node 与 BFF —— 如果你还想对比 Node 方案