feat: add FastAPI example project with CRUD API

- Add FastAPI application with health check and item CRUD endpoints
- Add Pydantic models for request/response validation
- Add pytest test suite with 5 passing tests
- Add project documentation and run script

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
root 2026-05-07 08:25:34 +08:00
parent 23e94ada3b
commit 67d8c6bf30
10 changed files with 185 additions and 0 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ __pycache__/
*.egg-info/
.eggs/
*.egg
.venv/
# 环境变量
.env

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# FastAPI Example
一个最小化的 FastAPI REST API 示例项目。
## 快速开始
```bash
# 创建虚拟环境并安装依赖
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 启动服务
./run.sh
# 或直接运行
uvicorn app.main:app --reload
```
## API 端点
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | /health | 健康检查 |
| GET | /items | 列出所有项目 |
| POST | /items | 创建新项目 |
| GET | /items/{id} | 获取单个项目 |
## 测试
```bash
source .venv/bin/activate
pytest tests/ -v
```
## API 文档
启动服务后访问:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc

1
app/__init__.py Normal file
View File

@ -0,0 +1 @@
# app package

35
app/main.py Normal file
View File

@ -0,0 +1,35 @@
from fastapi import FastAPI, HTTPException
from typing import Dict
from app.models import Item, ItemCreate
app = FastAPI(title="FastAPI Example")
# 内存存储
_items: Dict[int, Item] = {}
_item_id_counter = 0
@app.get("/health")
async def health_check():
return {"status": "ok"}
@app.post("/items", response_model=Item)
async def create_item(item: ItemCreate):
global _item_id_counter
_item_id_counter += 1
new_item = Item(id=_item_id_counter, **item.model_dump())
_items[_item_id_counter] = new_item
return new_item
@app.get("/items", response_model=list[Item])
async def list_items():
return list(_items.values())
@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
if item_id not in _items:
raise HTTPException(status_code=404, detail="Item not found")
return _items[item_id]

13
app/models.py Normal file
View File

@ -0,0 +1,13 @@
from pydantic import BaseModel
from typing import Optional
class Item(BaseModel):
id: Optional[int] = None
name: str
description: Optional[str] = None
class ItemCreate(BaseModel):
name: str
description: Optional[str] = None

View File

@ -0,0 +1,38 @@
# FastAPI 示例项目实现计划
日期2026-05-07
## 目标
搭建一个最小可运行的 FastAPI REST API 示例项目。
## 任务清单
### 批次 1项目基础
| # | 任务 | 文件 | 验证 | 预计时间 |
|---|------|------|------|----------|
| 1 | 创建 requirements.txt | requirements.txt | 文件存在 | 1min |
| 2 | 创建项目目录结构 | app/, tests/ | 目录存在 | 1min |
| 3 | 编写数据模型 | app/models.py | 导入成功 | 2min |
### 批次 2核心实现TDD
| # | 任务 | 文件 | 验证 | 预计时间 |
|---|------|------|------|----------|
| 4 | 写失败测试:健康检查 | tests/test_api.py | pytest FAIL | 2min |
| 5 | 实现健康检查端点 | app/main.py | pytest PASS | 2min |
| 6 | 写失败测试CRUD | tests/test_api.py | pytest FAIL | 3min |
| 7 | 实现 CRUD 端点 | app/routes.py | pytest PASS | 5min |
### 批次 3收尾
| # | 任务 | 文件 | 验证 | 预计时间 |
|---|------|------|------|----------|
| 8 | 添加启动脚本 | run.sh | 脚本可执行 | 1min |
| 9 | 更新文档 | README.md | 文件存在 | 2min |
## 验证命令
- 单测:`pytest tests/ -v`
- 启动:`uvicorn app.main:app --reload`
## 完成标准
- [ ] 所有测试通过
- [ ] API 可启动并响应
- [ ] 代码已提交

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
fastapi>=0.100.0
uvicorn[standard]>=0.23.0
pytest>=7.0.0
httpx>=0.24.0

3
run.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
source .venv/bin/activate
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

1
tests/__init__.py Normal file
View File

@ -0,0 +1 @@
# tests package

49
tests/test_api.py Normal file
View File

@ -0,0 +1,49 @@
from fastapi.testclient import TestClient
def test_health_check():
"""测试健康检查端点"""
from app.main import app
client = TestClient(app)
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_create_item():
"""测试创建项目"""
from app.main import app
client = TestClient(app)
response = client.post("/items", json={"name": "Test Item", "description": "A test"})
assert response.status_code == 200
data = response.json()
assert data["name"] == "Test Item"
assert data["id"] == 1
def test_list_items():
"""测试列出项目"""
from app.main import app
client = TestClient(app)
client.post("/items", json={"name": "Item 1"})
response = client.get("/items")
assert response.status_code == 200
assert len(response.json()) >= 1
def test_get_item():
"""测试获取单个项目"""
from app.main import app
client = TestClient(app)
created = client.post("/items", json={"name": "Item 2"}).json()
response = client.get(f"/items/{created['id']}")
assert response.status_code == 200
assert response.json()["name"] == "Item 2"
def test_get_item_not_found():
"""测试获取不存在的项目"""
from app.main import app
client = TestClient(app)
response = client.get("/items/99999")
assert response.status_code == 404