一、为什么从 Node.js 学 FastAPI 很容易
如果你写过 Node.js,尤其是用过 Express、Koa 或 Nest,那 FastAPI 对你来说其实并不陌生。它和 Node.js 服务端框架的很多理念高度相似:轻量、异步优先、用『装饰器/路由函数』组织接口、用中间件做通用处理、依赖生态丰富。
下面这张对比表能帮你快速建立映射:
| Node.js 中你熟悉的 | FastAPI 对应概念 |
|---|---|
| npm install 安装依赖 | pip install 安装依赖 |
| node_modules 项目依赖目录 | venv 虚拟环境 |
| node server.js 启动服务 | uvicorn main:app --reload 启动服务 |
| app.get("路径", handler) | @app.get("路径") 装饰器加函数 |
| req.query / req.params / req.body | 函数参数自动注入 query / path / body |
| Joi / zod 做参数校验 | Pydantic 模型做类型校验 |
| Express 中间件 | 依赖注入 Depends 和中间件 |
| res.json 返回数据 | 函数直接 return Python 字典 |
FastAPI 最大的特点是:你写完函数和类型注解,它就会自动帮我们完成参数解析、数据校验、类型转换、响应序列化,还会自动生成可交互的接口文档。接下来我们从环境搭建开始,一步步完成一个可运行的 TODO REST API。
二、环境准备:像 npm install 一样装好依赖
首先确认你的电脑上已经安装了 Python 3.9 以上版本。可以在终端执行:
python --version如果版本低于 3.9,建议先升级。确认完成后,按下面的步骤创建项目并安装依赖。这个过程的体验很像 npm 项目初始化。
2.1 创建项目目录并进入
mkdir fastapi-demo cd fastapi-demo2.2 创建并激活虚拟环境
虚拟环境可以理解为 Node.js 里的 node_modules,但它是一个独立的 Python 解释器环境,用来隔离不同项目的依赖。
python -m venv venv激活虚拟环境:
- Windows:
venv\Scripts\activate - macOS / Linux:
source venv/bin/activate
激活成功后,终端提示符前面通常会出现(venv)字样。
2.3 安装 FastAPI 和 Uvicorn
pip install "fastapi[standard]"这条命令会安装 FastAPI 以及推荐使用的 Uvicorn 服务器。Uvicorn 的作用类似 Node.js 里用 node 运行服务,只是它专门用来运行 ASGI 应用。
安装完成后,项目目录结构大致如下:
fastapi-demo ├── venv └── main.py三、第一个 FastAPI 应用:Hello World
创建main.py,写入以下代码:
from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root() -> dict: return {"message": "Hello World"}如果你写过 Express,这段代码的对应关系非常直观:
const express = require("express"); const app = express(); app.get("/", (req, res) => { res.json({ message: "Hello World" }); }); app.listen(3000);两者都是在实例化应用后,用get注册一个根路径处理器,最后返回 JSON。区别在于 Express 需要调用res.json,FastAPI 则直接返回 Python 字典,由框架自动序列化。
在终端启动服务:
uvicorn main:app --reload这里的main:app表示运行 main.py 文件中的 app 对象,--reload相当于监听文件变化自动重启,类似 nodemon。
打开浏览器访问 http://127.0.0.1:8000/,就能看到返回的 JSON 数据。
四、路由与请求方法:从 Express 路线图迁移
FastAPI 通过装饰器注册路由,常用方法包括@app.get、@app.post、@app.put、@app.delete、@app.patch。对于写 Node.js 的你来说,这就是 Express 路由写法的镜像。
@app.get("/items") def list_items() -> list: return [] @app.post("/items") def create_item() -> dict: return {"created": True}路由函数的名字可以随意取,真正决定地址的是装饰器里的路径字符串。同一个路径可以同时注册 GET 和 POST,分别处理不同的操作,和 RESTful 接口的设计思路一致。
五、请求参数:路径参数、查询参数、请求体
Node.js 里我们通常从req.params、req.query、req.body里拿参数。FastAPI 的做法更加直接:把这些值声明成函数参数,框架会根据参数名和类型自动完成注入和校验。
5.1 路径参数
@app.get("/items/{item_id}") def read_item(item_id: int) -> dict: return {"item_id": item_id}访问/items/42时,item_id会被自动解析为整数 42。如果传入/items/abc,FastAPI 会直接返回 422 校验错误,而不是让代码拿到一个字符串后崩溃。
5.2 查询参数
@app.get("/search") def search(q: str, limit: int = 10) -> dict: return {"q": q, "limit": limit}访问/search?q=fastapi&limit=20,函数会自动收到 q 和 limit。带默认值的参数属于可选参数,和 TypeScript 函数参数默认值的写法一致。
5.3 请求体
请求体通常配合 Pydantic 模型一起使用,示例见下一节。
六、Pydantic 数据校验:像 zod、Joi 一样的类型系统
Pydantic 是 FastAPI 官方推荐的数据校验库,作用类似 Node.js 生态里的 zod 或 Joi。通过定义模型类,可以把请求体的校验和类型声明统一起来。
from pydantic import BaseModel class Item(BaseModel): name: str price: float is_offer: bool = False @app.post("/items") def create_item(item: Item) -> dict: return {"name": item.name, "price": item.price}当客户端发送 POST 请求到 /items 时,FastAPI 会:
- 读取 JSON 请求体。
- 按照 Item 模型的字段完成类型校验和转换。
- 校验失败自动返回带详细信息的 422 错误。
- 校验成功则把 item 实例传入函数。
还可以定义可选字段和嵌套模型:
from typing import Optional class User(BaseModel): username: str email: str age: Optional[int] = None如果请求少传了 username 或 email,接口会直接返回校验错误,开发者不需要手动写一堆 if 判断。
七、依赖注入:FastAPI 最值得学的一招
Express 里很多重复逻辑会写成中间件或工具函数。FastAPI 提供了更优雅的依赖注入机制,通过Depends把公共逻辑复用起来。
from fastapi import Depends def get_db() -> list: return [{"id": 1, "title": "示例任务"}] @app.get("/todos") def list_todos(db: list = Depends(get_db)) -> list: return db任何需要数据库连接的接口,只要在函数参数里写db: list = Depends(get_db),FastAPI 就会自动调用get_db并把返回值注入。这种方式的好处是:公共逻辑只写一次,接口函数保持纯粹,也方便单元测试时替换依赖。
八、异常处理与 HTTPException
当资源不存在或请求不合法时,应该返回正确的 HTTP 状态码,而不是直接返回 200 加错误信息。FastAPI 使用HTTPException主动抛出错误:
from fastapi import HTTPException @app.get("/todos/{todo_id}") def get_todo(todo_id: int) -> dict: todo = next((t for t in todos if t["id"] == todo_id), None) if todo is None: raise HTTPException(status_code=404, detail="任务不存在") return todo这里的raise HTTPException相当于 Express 里的res.status(404).json({...}),但写法更符合 Python 习惯,并且会被 FastAPI 统一转换成标准的错误响应。
九、CORS 与中间件
前端和后端分离开发时,跨域问题很常见。在 Express 里我们通常用 cors 中间件解决,在 FastAPI 中同样可以添加 CORSMiddleware:
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], allow_methods=["*"], allow_headers=["*"], )这里的http://localhost:3000是示例前端地址,实际使用时替换成自己的前端域名即可。开发阶段为了省事,也可以把allow_origins设置为["*"]。
十、实战:构建一个完整的 TODO REST API
下面我们把前面的知识串起来,写一个支持增删改查的待办事项接口。先把完整代码放进main.py:
from typing import List from fastapi import FastAPI, HTTPException, status from pydantic import BaseModel app = FastAPI(title="FastAPI TODO 实战") class TodoCreate(BaseModel): title: str completed: bool = False class Todo(TodoCreate): id: int todos: List[Todo] = [] next_id: int = 1 @app.get("/") def read_root() -> dict: return {"message": "欢迎使用 FastAPI TODO API"} @app.get("/todos", response_model=List[Todo]) def list_todos() -> List[Todo]: return todos @app.post("/todos", response_model=Todo, status_code=status.HTTP_201_CREATED) def create_todo(todo: TodoCreate) -> Todo: global next_id new_todo = Todo(id=next_id, **todo.model_dump()) next_id += 1 todos.append(new_todo) return new_todo @app.get("/todos/{todo_id}", response_model=Todo) def get_todo(todo_id: int) -> Todo: todo = next((t for t in todos if t.id == todo_id), None) if todo is None: raise HTTPException(status_code=404, detail="任务不存在") return todo @app.put("/todos/{todo_id}", response_model=Todo) def update_todo(todo_id: int, todo: TodoCreate) -> Todo: old = next((t for t in todos if t.id == todo_id), None) if old is None: raise HTTPException(status_code=404, detail="任务不存在") old.title = todo.title old.completed = todo.completed return old @app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_todo(todo_id: int) -> None: global todos todo = next((t for t in todos if t.id == todo_id), None) if todo is None: raise HTTPException(status_code=404, detail="任务不存在") todos = [t for t in todos if t.id != todo_id]这个例子为了聚焦 FastAPI 概念,使用内存列表存储数据。重新启动服务后数据会清空,实际项目里把todos换成数据库即可。
10.1 启动并测试接口
uvicorn main:app --reload创建一条待办:
curl -X POST http://127.0.0.1:8000/todos \ -H "Content-Type: application/json" \ -d '{"title": "学习 FastAPI"}'查询所有待办:
curl http://127.0.0.1:8000/todos更新一条待办:
curl -X PUT http://127.0.0.1:8000/todos/1 \ -H "Content-Type: application/json" \ -d '{"title": "完成 FastAPI 实战", "completed": true}'删除一条待办,删除成功后响应体为空,状态码为 204:
curl -X DELETE http://127.0.0.1:8000/todos/1 -i除了 curl,你也可以用 Postman、Apifox 等工具来调试这些接口。
十一、自动文档:无需手动维护的 API 文档
FastAPI 会根据路由和 Pydantic 模型自动生成交互式接口文档。启动服务后,打开浏览器访问:
- Swagger UI:High Performance Web Crawler API - Swagger UI
- ReDoc:High Performance Web Crawler API - ReDoc
在 Swagger UI 页面里可以直接点击“Try it out”发送请求并查看响应,这对前后端联调非常有帮助,也不需要额外安装 Swagger 相关依赖。
十二、总结与下一步学习建议
通过这篇文章,我们从零搭建了 FastAPI 开发环境,完成了路由、参数解析、Pydantic 校验、依赖注入、异常处理、CORS 配置和完整的 TODO REST API。对于写过 Node.js 的开发者来说,你已经跨过了最陌生的那部分,剩下的更多是熟悉 Python 语法和生态习惯。
接下来建议按以下顺序继续学习:
- 把内存存储替换成 SQLite 或 PostgreSQL,了解 SQLModel、SQLAlchemy 的使用方式。
- 学习 Pydantic 的嵌套模型、字段约束和默认值的高级用法。
- 了解异步接口
async def与数据库异步驱动配合的场景。 - 学习 FastAPI 的测试客户端 TestClient,给接口写自动化测试。
- 尝试用 Docker 部署 FastAPI 服务,完成从开发到上线的完整链路。
学会 FastAPI 之后,你会发现