这是一个用于学习 FastAPI 框架的完整项目模板,包含了现代 Web API 开发中常用的功能和最佳实践。
- FastAPI 框架: 现代化的 Python Web 框架
- JWT 认证: 基于 JWT 的用户认证系统
- MongoDB 数据库: 使用 Motor 异步 MongoDB 驱动
- API 文档: 自动生成的 Swagger/OpenAPI 文档
- 依赖注入: 使用 FastAPI 的 Depends 系统
- 数据验证: Pydantic 模型验证
- CORS 支持: 跨域资源共享
- 日志系统: 结构化日志记录(Loguru)
- 异步支持: 全异步数据库操作
- 测试支持: 单元测试框架
- 代码重构: 统一的模型管理和类型定义
fastapi-learning-project/
├── app/ # 应用主目录
│ ├── api/ # API 路由
│ │ └── v1/ # API v1 版本
│ │ ├── endpoints/ # API 端点
│ │ └── api.py # 主路由
│ ├── core/ # 核心配置
│ │ ├── auth.py # 认证模块
│ │ ├── config.py # 配置管理
│ │ └── database.py # MongoDB 连接
│ ├── models/ # 数据模型
│ │ ├── common.py # 共享类型定义
│ │ ├── user.py # 用户模型
│ │ └── item.py # 物品模型
│ ├── services/ # 业务逻辑
│ └── utils/ # 工具函数
├── tests/ # 测试文件
├── main.py # 应用入口
├── run.py # 运行脚本
├── requirements.txt # 依赖包
└── README.md # 项目说明
项目默认配置:
- 数据库 URL:
mongodb://localhost:27017 - 数据库名称:
fastapi-learning-db - 端口:
27017
可在 app/core/config.py 中修改配置:
MONGODB_URL: str = "mongodb://localhost:27017"
MONGODB_DB_NAME: str = "fastapi-learning-db"# 使用 Homebrew 安装 MongoDB
brew tap mongodb/brew
brew install mongodb-community
# 或下载官方安装包
# https://www.mongodb.com/try/download/community# 使用 conda
conda create -n fastapi-learning python=3.9
conda activate fastapi-learning
# 或使用 venv
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windowspip install -r requirements.txt# 使用 Homebrew 启动 MongoDB
brew services start mongodb-community
# 或手动启动 MongoDB
mongod --dbpath /path/to/your/data/directory --port 27017
# 检查 MongoDB 是否运行
ps aux | grep mongodpip install -r requirements.txt# 开发模式(推荐)
python run.py
# 或直接运行主文件
python main.py
# 或使用 uvicorn
uvicorn main:app --reload --host 0.0.0.0 --port 8000- API 文档: http://localhost:8000/docs
- ReDoc 文档: http://localhost:8000/redoc
- 健康检查: http://localhost:8000/health
- 根路径: http://localhost:8000/ - 欢迎页面
- 受保护路由: http://localhost:8000/protected - 需要认证
POST /api/v1/auth/login- 用户登录POST /api/v1/auth/register- 用户注册GET /api/v1/auth/me- 获取当前用户信息
GET /- 欢迎页面GET /health- 健康检查GET /protected- 受保护的路由(需要认证)
GET /api/v1/users/- 获取用户列表GET /api/v1/users/{user_id}- 获取用户详情POST /api/v1/users/- 创建用户PUT /api/v1/users/{user_id}- 更新用户DELETE /api/v1/users/{user_id}- 删除用户
GET /api/v1/items/- 获取物品列表GET /api/v1/items/{item_id}- 获取物品详情POST /api/v1/items/- 创建物品PUT /api/v1/items/{item_id}- 更新物品DELETE /api/v1/items/{item_id}- 删除物品
curl -X POST "http://localhost:8000/api/v1/auth/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin123"curl -X POST "http://localhost:8000/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"username": "newuser",
"email": "newuser@example.com",
"password": "password123",
"is_active": true,
"is_superuser": false
}'curl -X GET "http://localhost:8000/api/v1/auth/me" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"curl -X GET "http://localhost:8000/api/v1/users/?skip=0&limit=10" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"curl -X GET "http://localhost:8000/api/v1/users/USER_ID_HERE" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"curl -X POST "http://localhost:8000/api/v1/users/" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0" \
-H "Content-Type: application/json" \
-d '{
"username": "testuser",
"email": "testuser@example.com",
"password": "password123",
"is_active": true,
"is_superuser": false
}'curl -X PUT "http://localhost:8000/api/v1/users/USER_ID_HERE" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0" \
-H "Content-Type: application/json" \
-d '{
"username": "updateduser",
"email": "updated@example.com",
"password": "newpassword123",
"is_active": true
}'curl -X DELETE "http://localhost:8000/api/v1/users/USER_ID_HERE" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"curl -X GET "http://localhost:8000/api/v1/items/?skip=0&limit=10" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"curl -X GET "http://localhost:8000/api/v1/items/ITEM_ID_HERE" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"curl -X POST "http://localhost:8000/api/v1/items/" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0" \
-H "Content-Type: application/json" \
-d '{
"title": "测试物品",
"description": "这是一个测试物品",
"price": 99.99
}'curl -X PUT "http://localhost:8000/api/v1/items/ITEM_ID_HERE" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0" \
-H "Content-Type: application/json" \
-d '{
"title": "更新后的物品",
"description": "这是更新后的描述",
"price": 199.99
}'curl -X DELETE "http://localhost:8000/api/v1/items/ITEM_ID_HERE" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc1MjU2NjkwN30.uwolFA7g0j3AYpqR4hqAZ5bTF8NNrDgFoQoAoPMG2K0"# 登录获取新token
TOKEN=$(curl -s -X POST "http://localhost:8000/api/v1/auth/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin123" | jq -r '.access_token')
echo "新token: $TOKEN"- Token 有效期:JWT token 有30分钟有效期,过期后需要重新登录获取新token
- 替换 ID:将
USER_ID_HERE和ITEM_ID_HERE替换为实际的用户ID和物品ID - 权限说明:
- 只有超级用户才能创建、更新、删除其他用户
- 用户只能修改自己创建的物品
- 所有接口都需要有效的 JWT token
# 查看详细响应
curl -v http://localhost:8000/
# 保存响应到文件
curl http://localhost:8000/ > response.json
# 检查 MongoDB 连接
mongosh# 运行所有测试
pytest
# 运行特定测试文件
pytest tests/test_auth.py
# 运行测试并显示覆盖率
pytest --cov=app@router.get("/protected")
async def protected_route(
current_user: User = Depends(get_current_user)
):
return {"user": current_user.username}class UserCreate(BaseModel):
username: str
email: EmailStr
password: str# 获取数据库实例
database = get_database()
# 获取集合
users_collection = database.users
# 查询用户
user = await users_collection.find_one({"username": "testuser"})
# 插入文档
result = await users_collection.insert_one(user_doc.dict(by_alias=True))
# 更新文档
await users_collection.update_one(
{"_id": ObjectId(user_id)},
{"$set": update_data}
)
# 删除文档
await users_collection.delete_one({"_id": ObjectId(user_id)})raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="用户不存在"
)# 共享类型定义 (app/models/common.py)
class PyObjectId(ObjectId):
"""自定义 ObjectId 类型"""
@classmethod
def __get_validators__(cls):
yield cls.validate
# 用户模型 (app/models/user.py)
class UserDocument(BaseModel):
id: Optional[PyObjectId] = Field(default_factory=PyObjectId, alias="_id")
username: str
created_at: datetime = Field(default_factory=get_china_time)
# 物品模型 (app/models/item.py)
class ItemDocument(BaseModel):
id: Optional[PyObjectId] = Field(default_factory=PyObjectId, alias="_id")
title: str
owner_id: PyObjectId
created_at: datetime = Field(default_factory=get_china_time)# 使用 black 格式化代码
black app/
# 使用 isort 排序导入
isort app/
# 使用 flake8 检查代码风格
flake8 app/# 连接到 MongoDB
mongosh
# 查看数据库
show dbs
# 使用项目数据库
use fastapi-learning-db
# 查看集合
show collections
# 查看用户数据
db.users.find()
# 查看物品数据
db.items.find()
# 查看索引
db.users.getIndexes()
db.items.getIndexes()欢迎提交 Issue 和 Pull Request!
MIT License