Claude Code 从空目录到完整 API 全记录
Claude Code 从空目录到完整 API 全记录
前几篇分别在老项目上改功能、重构、写测试。这篇更进一步——从一个空目录开始。
我要建一个书签管理 API。收藏网页、打标签、搜索、导出。需求不复杂,但涉及模型设计、路由分层、测试、Docker 部署、文档。从头手写大概半个下午。
这次全交给 Claude Code,看看一个空目录能变成什么。
需求描述
在一个空目录下启动 Claude Code,输入:
我要建一个书签管理 REST API,技术栈 FastAPI + SQLite + SQLAlchemy。
功能:
- POST /bookmarks — 添加书签(url, title, tags 数组)
- GET /bookmarks — 获取所有书签,支持 ?tag=python 过滤
- GET /bookmarks/{id} — 获取单个书签
- DELETE /bookmarks/{id} — 删除书签
- GET /bookmarks/export — 导出为 JSON
- GET /health — 健康检查
要求:
- URL 要做基本验证(http/https 开头)
- tags 去重
- 带 pytest 测试,用 parametrize
- Dockerfile + docker-compose.yml
- README.md 包含安装和使用说明
- 所有测试通过后才能算完成
第一步:设计架构
Claude Code 没有直接撸代码。它先做了规划:
→ Project: bookmark-api
→ Plan:
1. Project structure:
bookmark_api/
├── main.py # FastAPI app entry
├── models.py # SQLAlchemy Bookmark model
├── schemas.py # Pydantic request/response schemas
├── database.py # DB connection + session
├── routes.py # API endpoints
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── README.md
└── tests/
├── conftest.py # fixtures (test client, test DB)
└── test_bookmarks.py
2. Data model:
Bookmark: id, url, title, tags (JSON array), created_at
3. Key decisions:
- tags stored as JSON string in SQLite (no separate tags table
for simplicity — this is a lightweight personal tool)
- URL validation via Pydantic field validator
- Test DB uses SQLite in-memory with session override
这个架构选择值得注意:tags 用 JSON 字符串存而不是独立表。Claude Code 给的判断依据是"轻量个人工具,不需要复杂关系查询"。如果是一个团队协作的标签系统,它会用第三范式。
第二步:一层一层建
Claude Code 从底层往上层建,每建一层跑一次验证。
database.py — 数据库连接层
[+] bookmark_api/database.py
- SQLAlchemy engine with SQLite
- SessionLocal factory
- get_db dependency
- Base declarative base
models.py — 数据模型
[+] bookmark_api/models.py
class Bookmark(Base):
id: int (PK)
url: str (unique=True, indexed)
title: str
tags: str (JSON-encoded)
created_at: datetime (default utcnow)
建完 model,Claude Code 立刻验证表能正常创建:
→ Tool: Bash
$ python -c "from bookmark_api.database import engine, Base; \
Base.metadata.create_all(engine); print('Tables created OK')"
→ Tables created OK
schemas.py — 请求/响应模型
[+] bookmark_api/schemas.py
BookmarkCreate: url (HttpUrl), title (str, 1-200), tags (list[str])
BookmarkResponse: id, url, title, tags, created_at
→ URL validation via Pydantic HttpUrl type
→ tags deduplication in model validator
用了 Pydantic 的 HttpUrl 类型做 URL 验证,tag 去重用 set() 在 validator 里处理。
routes.py — API 路由
[+] bookmark_api/routes.py
POST /bookmarks — create bookmark, return 201
GET /bookmarks — list all, ?tag= filter
GET /bookmarks/{id} — get by ID, 404 if missing
DELETE /bookmarks/{id} — delete, 404 if missing
GET /bookmarks/export — export all as JSON with metadata
GET /health — {"status": "ok"}
GET /bookmarks 的 tag 过滤是这样实现的:
@app.get("/bookmarks")
def list_bookmarks(tag: str | None = None, db: Session = Depends(get_db)):
query = db.query(Bookmark)
if tag:
query = query.filter(Bookmark.tags.contains(f'"{tag}"'))
return query.order_by(Bookmark.created_at.desc()).all()
没有用原始的 LIKE '%tag%',而是 contains(f'"{tag}"')——因为 tags 存的是 ["python", "web"] 这种 JSON 字符串,用引号包裹匹配避免了 “py” 匹配到 “python” 的部分字符串问题。
main.py — 入口
[+] bookmark_api/main.py
- FastAPI app with CORS middleware
- Include router
- Startup event: create tables
第三步:写测试
每个路由都配了测试。Claude Code 用了 conftest.py 里的 fixture 来管理测试数据库和应用客户端。
[+] tests/conftest.py
- app fixture: creates FastAPI test app
- client fixture: TestClient for HTTP testing
- db_session fixture: overrides get_db with in-memory SQLite
[+] tests/test_bookmarks.py
test_create_bookmark_success — 正常创建,验证 201
test_create_bookmark_invalid_url — 非法 URL → 422
test_create_bookmark_empty_title — 空标题 → 422
test_create_bookmark_duplicate_url — 重复 URL → 409
test_list_bookmarks_empty — 空数据库 → 空列表
test_list_bookmarks_with_data — 有数据 → 返回列表
test_list_bookmarks_filter_by_tag — tag 过滤
test_get_bookmark_by_id — 按 ID 查
test_get_bookmark_404 — 不存在 → 404
test_delete_bookmark — 删除成功 → 204
test_delete_bookmark_404 — 删除不存在 → 404
test_export_bookmarks — 导出 JSON → 验证结构
test_health_check — 健康检查
跑测试:
→ pytest tests/ -v
13 passed in 0.47s
13 个测试,一次全绿。
第四步:Docker 和文档
代码和测试都好了。Claude Code 接着建了部署和文档。
Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "bookmark_api.main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml
services:
api:
build: .
ports:
- "8000:8000"
volumes:
- ./data:/app/data
environment:
- DATABASE_URL=sqlite:///data/bookmarks.db
README.md
不是那种 “A simple bookmark API” 两行就算了的 README。它包含了:项目简介、技术栈、安装步骤、API 端点表格(含请求/响应示例)、Docker 部署命令、测试运行指令、项目结构树。
最终的项目结构
bookmark-api/
├── bookmark_api/
│ ├── __init__.py
│ ├── main.py (16 lines — FastAPI app)
│ ├── database.py (22 lines — SQLAlchemy config)
│ ├── models.py (18 lines — Bookmark model)
│ ├── schemas.py (35 lines — Pydantic schemas)
│ └── routes.py (68 lines — CRUD + export + health)
├── tests/
│ ├── __init__.py
│ ├── conftest.py (35 lines — fixtures)
│ └── test_bookmarks.py (112 lines — 13 tests)
├── requirements.txt (4 dependencies)
├── Dockerfile
├── docker-compose.yml
└── README.md (55 lines)
总代码量:~360 行 Python + 配置。从空目录到完整项目,大约 20 分钟。
跑起来:
$ docker-compose up -d
$ curl http://localhost:8000/health
{"status": "ok"}
$ curl -X POST http://localhost:8000/bookmarks \
-H 'Content-Type: application/json' \
-d '{"url": "https://fastapi.tiangolo.com", "title": "FastAPI Docs", "tags": ["python", "web"]}'
{"id": 1, "url": "https://fastapi.tiangolo.com", ...}
$ curl http://localhost:8000/bookmarks?tag=python
[{"id": 1, ...}]
$ curl http://localhost:8000/bookmarks/export
{"exported_at": "2026-05-11T...", "count": 1, "bookmarks": [...]}
Claude Code 在"从零搭建"中的几个亮点
先规划,再动手。 它不是收到需求就开始写 main.py。先分析了模型关系、存储方案、目录结构,然后才动刀。这个"设计方案→征求认可→执行"的过程,跟团队里的技术方案评审很像。
从底层往上层建。 database → models → schemas → routes → tests。每一层建完都做验证,确保依赖正确。这种顺序跟你手写时的思路一致——你不会先写 Controller 再写 Model。
细节经得起看。 contains(f'"{tag}"') 这个 JSON 标签匹配方式,说明了它理解了 tags 的存储格式(JSON 数组)以及 LIKE 匹配的陷阱。这不是"写完就行",是"写好才行"。
文档质量。 README 不是模板填空。有具体的 curl 示例、docker-compose 命令、API 表格。一个接手的人看 README 就能跑起来。
踩到的坑
1. 第一次架构方案要审核
Claude Code 提案的架构,我会快速扫一眼。这次 tags 用 JSON 存储的方案我认同——如果不同意,在这步纠正比重写代码省太多时间。
2. 需求描述里的"不做什么"同样重要
我在需求里写了范围(6 个端点),Claude Code 不会自己加东西。如果只说"书签 API",它可能加上用户认证、分页、全文搜索——功能蔓延。明确边界比写详细需求更重要。
3. 分步验证比一次性全部建完靠谱
建一个模块、跑一下、确认没问题、继续。虽然 Claude Code 也支持一次性生成全部代码,但分层验证每一层都能拦住问题。
什么样的项目适合 AI 从零建
不是所有项目都适合这么搞。能成功的场景有几个共同点:
范围明确——你知道要什么。需求不明确的时候,AI 会帮你"想"需求,但想出来的跟你真正需要的差距可能很大。
模式常见——REST API、CRUD、数据库操作。AI 见过的模式越多,代码质量越高。如果你的需求涉及一个冷门协议或者公司内部框架,AI 帮不上忙。
规模不大——单服务应用,几个到十几个文件。微服务架构、多仓库协同的复杂项目,不是 AI 现在能独立搞定的。
下一篇
从需求描述到项目落地,prompt 的质量直接影响结果。下一篇聊 Prompt Engineering——CLAUDE.md 怎么配、怎么给 AI 设定代码风格、上下文管理的实战技巧。
欢迎大家评论区交流!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)