zhanymkanov /fastapi-best-practices
FastAPI Best Practices and Conventions we used at our startup
★ 18,308⑂ 1,332
18,308
stars
+20+22+165
1,332
forks
–
watchers
–
prs
–
issues
–
releases
–
discussions
README
Fast Api最佳实践指南
这是我在初创公司使用的一系列最佳实践和约定。
在过去几年的生产实践中,我们做过一些好的和不好的决策,这些决策极大地影响了开发者体验。其中一些经验值得分享。
目录
- Fast Api最佳实践指南
- 目录
- 项目结构
- 异步路由
- I/O密集型任务
- CPU密集型任务
- Pydantic
- 大量使用Pydantic
- 自定义基础模型
- 拆分Pydantic BaseSettings
- 依赖项
- 超越依赖注入
- 链式依赖
- 拆分并复用依赖项。依赖调用会被缓存
- 优先使用async依赖项
- 其他
- 遵循REST规范
- FastAPI响应序列化
- 如果必须使用同步SDK,请在线程池中运行它。
- ValueErrors可能会变成Pydantic ValidationError
- 文档
- 迁移工具Alembic
- 设置数据库键命名约定
- SQL优先,Pydantic次之
- 从一开始就设置异步测试客户端
- 使用ruff
- 额外部分
项目结构
项目结构有很多种,但最好的结构是一致、直观且没有意外的。
许多示例项目和教程按文件类型(如crud、routers、models)划分项目,这种方式对于微服务或范围较小的项目很有效。但是,这种方法并不适合我们这个包含许多领域和模块的单体应用。
我发现对于这类情况,更具可扩展性和可演进性的结构是受Netflix的Dispatch启发,并做了一些小修改。
fastapi-project
├── alembic/
├── src
│ ├── auth
│ │ ├── router.py
│ │ ├── schemas.py pydantic模型
│ │ ├── models.py 数据库模型
│ │ ├── dependencies.py
│ │ ├── config.py 本地配置
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ └── utils.py
│ ├── aws
│ │ ├── client.py 用于外部服务通信的客户端模型
│ │ ├── schemas.py
│ │ ├── config.py
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ └── utils.py
│ ├── posts
│ │ ├── router.py
│ │ ├── schemas.py
│ │ ├── models.py
│ │ ├── dependencies.py
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ └── utils.py
│ ├── config.py 全局配置
│ ├── models.py 全局模型
│ ├── exceptions.py 全局异常
│ ├── pagination.py 全局模块,如分页
│ ├── database.py 数据库连接相关内容
│ └── main.py
├── tests/
│ ├── auth
│ ├── aws
│ └── posts
├── templates/
│ └── index.html
├── requirements
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
├── .env
├── .gitignore
├── logging.ini
└── alembic.ini
1. 将所有领域目录存储在src文件夹中
1. src/ - 应用的最高级别,包含通用模型、配置和常量等。
2. src/main.py - 项目的根文件,用于初始化FastAPI应用
2. 每个包都有自己的路由、模式、模型等。
1. router.py - 每个模块的核心,包含所有端点
2. schemas.py - 用于pydantic模型
3. models.py - 用于数据库模型
4. service.py - 模块特定的业务逻辑
5. dependencies.py - 路由依赖项
6. constants.py - 模块特定的常量和错误代码
7. config.py - 例如环境变量
8. utils.py - 非业务逻辑函数,例如响应规范化、数据丰富等
9. exceptions.py - 模块特定的异常,例如PostNotFound、InvalidUserData
3. 当包需要其他包的服务、依赖项或常量时,使用显式的模块名导入
python
from src.auth import constants as auth_constants
from src.notifications import service as notification_service
from src.posts.constants import ErrorCode as PostsErrorCode 以防每个包的constants模块中都有标准的ErrorCode
异步路由
FastAPI首先是一个异步框架。它设计用于处理异步I/O操作,这也是它如此快速的原因。
然而,FastAPI并不限制你只能使用async路由,开发者也可以使用同步路由。这可能会让初学者误以为它们是一样的,但实际上并非如此。
I/O密集型任务
在底层,FastAPI可以有效地处理异步和同步I/O操作。
- FastAPI在线程池中运行同步路由,阻塞的I/O操作不会阻止事件循环执行任务。
- 如果路由定义为async,那么它会通过await正常调用,FastAPI相信你只会执行非阻塞的I/O操作。
需要注意的是,如果你违反了这种信任,在异步路由中执行阻塞操作,事件循环将无法在阻塞操作完成之前运行后续任务。
python
import asyncio
import time
from fastapi import APIRouter
router = APIRouter()
@router.get("/terrible-ping")
async def terrible_ping():
time.sleep(10) 10秒的I/O阻塞操作,整个进程都会被阻塞
return {"pong": True}
@router.get(