mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-13 18:00:35 +08:00
docs: salvage FastAPI review patterns
This commit is contained in:
parent
1c06ad9524
commit
d52cdccb0d
@ -11,7 +11,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ecc",
|
"name": "ecc",
|
||||||
"source": "./",
|
"source": "./",
|
||||||
"description": "The most comprehensive Claude Code plugin — 50 agents, 188 skills, 68 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
|
"description": "The most comprehensive Claude Code plugin — 51 agents, 189 skills, 69 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning",
|
||||||
"version": "2.0.0-rc.1",
|
"version": "2.0.0-rc.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Affaan Mustafa",
|
"name": "Affaan Mustafa",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ecc",
|
"name": "ecc",
|
||||||
"version": "2.0.0-rc.1",
|
"version": "2.0.0-rc.1",
|
||||||
"description": "Battle-tested Claude Code plugin for engineering teams — 50 agents, 188 skills, 68 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
|
"description": "Battle-tested Claude Code plugin for engineering teams — 51 agents, 189 skills, 69 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Affaan Mustafa",
|
"name": "Affaan Mustafa",
|
||||||
"url": "https://x.com/affaanmustafa"
|
"url": "https://x.com/affaanmustafa"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Everything Claude Code (ECC) — Agent Instructions
|
# Everything Claude Code (ECC) — Agent Instructions
|
||||||
|
|
||||||
This is a **production-ready AI coding plugin** providing 50 specialized agents, 188 skills, 68 commands, and automated hook workflows for software development.
|
This is a **production-ready AI coding plugin** providing 51 specialized agents, 189 skills, 69 commands, and automated hook workflows for software development.
|
||||||
|
|
||||||
**Version:** 2.0.0-rc.1
|
**Version:** 2.0.0-rc.1
|
||||||
|
|
||||||
@ -145,9 +145,9 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
agents/ — 50 specialized subagents
|
agents/ — 51 specialized subagents
|
||||||
skills/ — 188 workflow skills and domain knowledge
|
skills/ — 189 workflow skills and domain knowledge
|
||||||
commands/ — 68 slash commands
|
commands/ — 69 slash commands
|
||||||
hooks/ — Trigger-based automations
|
hooks/ — Trigger-based automations
|
||||||
rules/ — Always-follow guidelines (common + per-language)
|
rules/ — Always-follow guidelines (common + per-language)
|
||||||
scripts/ — Cross-platform Node.js utilities
|
scripts/ — Cross-platform Node.js utilities
|
||||||
|
|||||||
16
README.md
16
README.md
@ -350,7 +350,7 @@ If you stacked methods, clean up in this order:
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**That's it!** You now have access to 50 agents, 188 skills, and 68 legacy command shims.
|
**That's it!** You now have access to 51 agents, 189 skills, and 69 legacy command shims.
|
||||||
|
|
||||||
### Dashboard GUI
|
### Dashboard GUI
|
||||||
|
|
||||||
@ -448,7 +448,7 @@ everything-claude-code/
|
|||||||
| |-- plugin.json # Plugin metadata and component paths
|
| |-- plugin.json # Plugin metadata and component paths
|
||||||
| |-- marketplace.json # Marketplace catalog for /plugin marketplace add
|
| |-- marketplace.json # Marketplace catalog for /plugin marketplace add
|
||||||
|
|
|
|
||||||
|-- agents/ # 50 specialized subagents for delegation
|
|-- agents/ # 51 specialized subagents for delegation
|
||||||
| |-- planner.md # Feature implementation planning
|
| |-- planner.md # Feature implementation planning
|
||||||
| |-- architect.md # System design decisions
|
| |-- architect.md # System design decisions
|
||||||
| |-- tdd-guide.md # Test-driven development
|
| |-- tdd-guide.md # Test-driven development
|
||||||
@ -1336,9 +1336,9 @@ The configuration is automatically detected from `.opencode/opencode.json`.
|
|||||||
|
|
||||||
| Feature | Claude Code | OpenCode | Status |
|
| Feature | Claude Code | OpenCode | Status |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| Agents | PASS: 50 agents | PASS: 12 agents | **Claude Code leads** |
|
| Agents | PASS: 51 agents | PASS: 12 agents | **Claude Code leads** |
|
||||||
| Commands | PASS: 68 commands | PASS: 31 commands | **Claude Code leads** |
|
| Commands | PASS: 69 commands | PASS: 31 commands | **Claude Code leads** |
|
||||||
| Skills | PASS: 188 skills | PASS: 37 skills | **Claude Code leads** |
|
| Skills | PASS: 189 skills | PASS: 37 skills | **Claude Code leads** |
|
||||||
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
| Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** |
|
||||||
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
| Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** |
|
||||||
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
| MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** |
|
||||||
@ -1441,9 +1441,9 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e
|
|||||||
|
|
||||||
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
| Feature | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|---------|------------|------------|-----------|----------|
|
|---------|------------|------------|-----------|----------|
|
||||||
| **Agents** | 50 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
| **Agents** | 51 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 |
|
||||||
| **Commands** | 68 | Shared | Instruction-based | 31 |
|
| **Commands** | 69 | Shared | Instruction-based | 31 |
|
||||||
| **Skills** | 188 | Shared | 10 (native format) | 37 |
|
| **Skills** | 189 | Shared | 10 (native format) | 37 |
|
||||||
| **Hook Events** | 8 types | 15 types | None yet | 11 types |
|
| **Hook Events** | 8 types | 15 types | None yet | 11 types |
|
||||||
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks |
|
| **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks |
|
||||||
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions |
|
| **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions |
|
||||||
|
|||||||
@ -160,7 +160,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**完成!** 你现在可以使用 50 个代理、188 个技能和 68 个命令。
|
**完成!** 你现在可以使用 51 个代理、189 个技能和 69 个命令。
|
||||||
|
|
||||||
### multi-* 命令需要额外配置
|
### multi-* 命令需要额外配置
|
||||||
|
|
||||||
|
|||||||
@ -152,6 +152,7 @@ commands:
|
|||||||
- cpp-review
|
- cpp-review
|
||||||
- cpp-test
|
- cpp-test
|
||||||
- evolve
|
- evolve
|
||||||
|
- fastapi-review
|
||||||
- feature-dev
|
- feature-dev
|
||||||
- flutter-build
|
- flutter-build
|
||||||
- flutter-review
|
- flutter-review
|
||||||
|
|||||||
70
agents/fastapi-reviewer.md
Normal file
70
agents/fastapi-reviewer.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
name: fastapi-reviewer
|
||||||
|
description: Reviews FastAPI applications for async correctness, dependency injection, Pydantic schemas, security, OpenAPI quality, testing, and production readiness.
|
||||||
|
tools: ["Read", "Grep", "Glob", "Bash"]
|
||||||
|
model: sonnet
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a senior FastAPI reviewer focused on production Python APIs.
|
||||||
|
|
||||||
|
## Review Scope
|
||||||
|
|
||||||
|
- FastAPI app construction, routing, middleware, and exception handling.
|
||||||
|
- Pydantic request, update, and response models.
|
||||||
|
- Async database and HTTP patterns.
|
||||||
|
- Dependency injection for database sessions, auth, pagination, and settings.
|
||||||
|
- Authentication, authorization, CORS, rate limits, logging, and secret handling.
|
||||||
|
- Test dependency overrides and client setup.
|
||||||
|
- OpenAPI metadata and generated docs.
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- Non-FastAPI frameworks unless they directly interact with the FastAPI app.
|
||||||
|
- Broad Python style review already covered by `python-reviewer`.
|
||||||
|
- Dependency additions without a concrete problem and maintenance rationale.
|
||||||
|
|
||||||
|
## Review Workflow
|
||||||
|
|
||||||
|
1. Locate the app entry point, usually `main.py`, `app.py`, or `app/main.py`.
|
||||||
|
2. Identify routers, schemas, dependencies, database session setup, and tests.
|
||||||
|
3. Run available local checks when safe, such as `pytest`, `ruff`, `mypy`, or `uv run pytest`.
|
||||||
|
4. Review the changed files first, then inspect adjacent definitions needed to prove findings.
|
||||||
|
5. Report only actionable issues with file and line references when available.
|
||||||
|
|
||||||
|
## Finding Priorities
|
||||||
|
|
||||||
|
### Critical
|
||||||
|
|
||||||
|
- Hardcoded secrets or tokens.
|
||||||
|
- SQL built through string interpolation.
|
||||||
|
- Passwords, token hashes, or internal auth fields exposed in response models.
|
||||||
|
- Auth dependencies that can be bypassed or do not validate expiry/signature.
|
||||||
|
|
||||||
|
### High
|
||||||
|
|
||||||
|
- Blocking database or HTTP clients inside async routes.
|
||||||
|
- Database sessions created inline in handlers instead of dependencies.
|
||||||
|
- Test overrides targeting the wrong dependency.
|
||||||
|
- `allow_origins=["*"]` combined with credentialed CORS.
|
||||||
|
- Missing request validation for write endpoints.
|
||||||
|
|
||||||
|
### Medium
|
||||||
|
|
||||||
|
- Missing pagination on list endpoints.
|
||||||
|
- OpenAPI docs missing response models or error response descriptions.
|
||||||
|
- Duplicated route logic that should move into a service/dependency.
|
||||||
|
- Missing timeout settings for external HTTP clients.
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
```text
|
||||||
|
[SEVERITY] Short issue title
|
||||||
|
File: path/to/file.py:42
|
||||||
|
Issue: What is wrong and why it matters.
|
||||||
|
Fix: Concrete change to make.
|
||||||
|
```
|
||||||
|
|
||||||
|
End with:
|
||||||
|
|
||||||
|
- `Tests checked:` commands run or why they were skipped.
|
||||||
|
- `Residual risk:` anything important that could not be verified.
|
||||||
39
commands/fastapi-review.md
Normal file
39
commands/fastapi-review.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
description: Review a FastAPI application for architecture, async correctness, dependency injection, Pydantic schemas, security, performance, and testability.
|
||||||
|
---
|
||||||
|
|
||||||
|
# FastAPI Review
|
||||||
|
|
||||||
|
Invoke the `fastapi-reviewer` agent for a focused FastAPI review.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```text
|
||||||
|
/fastapi-review [file-or-directory]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Review Areas
|
||||||
|
|
||||||
|
- App factory, router boundaries, middleware, and exception handlers.
|
||||||
|
- Pydantic request and response schema separation.
|
||||||
|
- Dependency injection for database sessions, auth, pagination, and settings.
|
||||||
|
- Async database and external HTTP patterns.
|
||||||
|
- CORS, auth, rate limits, logging, and secret handling.
|
||||||
|
- OpenAPI metadata and documented response models.
|
||||||
|
- Test client setup and dependency overrides.
|
||||||
|
|
||||||
|
## Expected Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
[SEVERITY] Short issue title
|
||||||
|
File: path/to/file.py:42
|
||||||
|
Issue: What is wrong and why it matters.
|
||||||
|
Fix: Concrete change to make.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- Agent: `fastapi-reviewer`
|
||||||
|
- Skill: `fastapi-patterns`
|
||||||
|
- Command: `/python-review`
|
||||||
|
- Skill: `security-scan`
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# Everything Claude Code (ECC) — 智能体指令
|
# Everything Claude Code (ECC) — 智能体指令
|
||||||
|
|
||||||
这是一个**生产就绪的 AI 编码插件**,提供 50 个专业代理、188 项技能、68 条命令以及自动化钩子工作流,用于软件开发。
|
这是一个**生产就绪的 AI 编码插件**,提供 51 个专业代理、189 项技能、69 条命令以及自动化钩子工作流,用于软件开发。
|
||||||
|
|
||||||
**版本:** 2.0.0-rc.1
|
**版本:** 2.0.0-rc.1
|
||||||
|
|
||||||
@ -146,9 +146,9 @@
|
|||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
```
|
```
|
||||||
agents/ — 50 个专业子代理
|
agents/ — 51 个专业子代理
|
||||||
skills/ — 188 个工作流技能和领域知识
|
skills/ — 189 个工作流技能和领域知识
|
||||||
commands/ — 68 个斜杠命令
|
commands/ — 69 个斜杠命令
|
||||||
hooks/ — 基于触发的自动化
|
hooks/ — 基于触发的自动化
|
||||||
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
rules/ — 始终遵循的指导方针(通用 + 每种语言)
|
||||||
scripts/ — 跨平台 Node.js 实用工具
|
scripts/ — 跨平台 Node.js 实用工具
|
||||||
|
|||||||
@ -224,7 +224,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/"
|
|||||||
/plugin list ecc@ecc
|
/plugin list ecc@ecc
|
||||||
```
|
```
|
||||||
|
|
||||||
**搞定!** 你现在可以使用 50 个智能体、188 项技能和 68 个命令了。
|
**搞定!** 你现在可以使用 51 个智能体、189 项技能和 69 个命令了。
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
@ -1132,9 +1132,9 @@ opencode
|
|||||||
|
|
||||||
| 功能特性 | Claude Code | OpenCode | 状态 |
|
| 功能特性 | Claude Code | OpenCode | 状态 |
|
||||||
|---------|-------------|----------|--------|
|
|---------|-------------|----------|--------|
|
||||||
| 智能体 | PASS: 50 个 | PASS: 12 个 | **Claude Code 领先** |
|
| 智能体 | PASS: 51 个 | PASS: 12 个 | **Claude Code 领先** |
|
||||||
| 命令 | PASS: 68 个 | PASS: 31 个 | **Claude Code 领先** |
|
| 命令 | PASS: 69 个 | PASS: 31 个 | **Claude Code 领先** |
|
||||||
| 技能 | PASS: 188 项 | PASS: 37 项 | **Claude Code 领先** |
|
| 技能 | PASS: 189 项 | PASS: 37 项 | **Claude Code 领先** |
|
||||||
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
| 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** |
|
||||||
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
| 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** |
|
||||||
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
| MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** |
|
||||||
@ -1240,9 +1240,9 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
|
|||||||
|
|
||||||
| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
| 功能特性 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|
||||||
|---------|------------|------------|-----------|----------|
|
|---------|------------|------------|-----------|----------|
|
||||||
| **智能体** | 50 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
| **智能体** | 51 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 |
|
||||||
| **命令** | 68 | 共享 | 基于指令 | 31 |
|
| **命令** | 69 | 共享 | 基于指令 | 31 |
|
||||||
| **技能** | 188 | 共享 | 10 (原生格式) | 37 |
|
| **技能** | 189 | 共享 | 10 (原生格式) | 37 |
|
||||||
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
|
||||||
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
| **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 |
|
||||||
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
| **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 |
|
||||||
|
|||||||
@ -125,6 +125,7 @@
|
|||||||
"skills/django-tdd",
|
"skills/django-tdd",
|
||||||
"skills/django-verification",
|
"skills/django-verification",
|
||||||
"skills/dotnet-patterns",
|
"skills/dotnet-patterns",
|
||||||
|
"skills/fastapi-patterns",
|
||||||
"skills/frontend-patterns",
|
"skills/frontend-patterns",
|
||||||
"skills/frontend-slides",
|
"skills/frontend-slides",
|
||||||
"skills/golang-patterns",
|
"skills/golang-patterns",
|
||||||
|
|||||||
@ -146,6 +146,7 @@
|
|||||||
"skills/evm-token-decimals/",
|
"skills/evm-token-decimals/",
|
||||||
"skills/exa-search/",
|
"skills/exa-search/",
|
||||||
"skills/fal-ai-media/",
|
"skills/fal-ai-media/",
|
||||||
|
"skills/fastapi-patterns/",
|
||||||
"skills/finance-billing-ops/",
|
"skills/finance-billing-ops/",
|
||||||
"skills/foundation-models-on-device/",
|
"skills/foundation-models-on-device/",
|
||||||
"skills/frontend-patterns/",
|
"skills/frontend-patterns/",
|
||||||
|
|||||||
58
rules/python/fastapi.md
Normal file
58
rules/python/fastapi.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
- "**/app/**/*.py"
|
||||||
|
- "**/fastapi/**/*.py"
|
||||||
|
- "**/*_api.py"
|
||||||
|
---
|
||||||
|
# FastAPI Rules
|
||||||
|
|
||||||
|
Use these rules for FastAPI projects alongside the general Python rules.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
- Put app construction in `create_app()`.
|
||||||
|
- Keep routers thin; move persistence and business behavior into services or CRUD helpers.
|
||||||
|
- Keep request schemas, update schemas, and response schemas separate.
|
||||||
|
- Keep database sessions and auth in dependencies.
|
||||||
|
|
||||||
|
## Async
|
||||||
|
|
||||||
|
- Use `async def` for endpoints that perform I/O.
|
||||||
|
- Use async database and HTTP clients from async endpoints.
|
||||||
|
- Do not call `requests`, sync SQLAlchemy sessions, or blocking file/network operations from async routes.
|
||||||
|
|
||||||
|
## Dependency Injection
|
||||||
|
|
||||||
|
```python
|
||||||
|
@router.get("/users/{user_id}")
|
||||||
|
async def get_user(
|
||||||
|
user_id: str,
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not create `SessionLocal()` or long-lived clients inside route handlers.
|
||||||
|
|
||||||
|
## Schemas
|
||||||
|
|
||||||
|
- Never include passwords, password hashes, access tokens, refresh tokens, or internal auth state in response models.
|
||||||
|
- Use `response_model` on endpoints that return application data.
|
||||||
|
- Use field constraints instead of hand-written validation when Pydantic can express the rule.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
- Keep CORS origins environment-specific.
|
||||||
|
- Do not combine wildcard origins with credentialed CORS.
|
||||||
|
- Validate JWT expiry, issuer, audience, and algorithm.
|
||||||
|
- Rate-limit auth and write-heavy endpoints.
|
||||||
|
- Redact credentials, cookies, authorization headers, and tokens from logs.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Override the exact dependency used by `Depends`.
|
||||||
|
- Clear `app.dependency_overrides` after tests.
|
||||||
|
- Prefer async test clients for async applications.
|
||||||
|
|
||||||
|
See skill: `fastapi-patterns`.
|
||||||
327
skills/fastapi-patterns/SKILL.md
Normal file
327
skills/fastapi-patterns/SKILL.md
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
---
|
||||||
|
name: fastapi-patterns
|
||||||
|
description: FastAPI patterns for async APIs, dependency injection, Pydantic request and response models, OpenAPI docs, tests, security, and production readiness.
|
||||||
|
origin: community
|
||||||
|
---
|
||||||
|
|
||||||
|
# FastAPI Patterns
|
||||||
|
|
||||||
|
Production-oriented patterns for FastAPI services.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- Building or reviewing a FastAPI app.
|
||||||
|
- Splitting routers, schemas, dependencies, and database access.
|
||||||
|
- Writing async endpoints that call a database or external service.
|
||||||
|
- Adding authentication, authorization, OpenAPI docs, tests, or deployment settings.
|
||||||
|
- Checking a FastAPI PR for copy-pasteable examples and production risks.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
Treat the FastAPI app as a thin HTTP layer over explicit dependencies and service code:
|
||||||
|
|
||||||
|
- `main.py` owns app construction, middleware, exception handlers, and router registration.
|
||||||
|
- `schemas/` owns Pydantic request and response models.
|
||||||
|
- `dependencies.py` owns database, auth, pagination, and request-scoped dependencies.
|
||||||
|
- `services/` or `crud/` owns business and persistence operations.
|
||||||
|
- `tests/` overrides dependencies instead of opening production resources.
|
||||||
|
|
||||||
|
Prefer small routers and explicit `response_model` declarations. Keep raw ORM objects, secrets, and framework globals out of response schemas.
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
app/
|
||||||
|
|-- main.py
|
||||||
|
|-- config.py
|
||||||
|
|-- dependencies.py
|
||||||
|
|-- exceptions.py
|
||||||
|
|-- api/
|
||||||
|
| `-- routes/
|
||||||
|
| |-- users.py
|
||||||
|
| `-- health.py
|
||||||
|
|-- core/
|
||||||
|
| |-- security.py
|
||||||
|
| `-- middleware.py
|
||||||
|
|-- db/
|
||||||
|
| |-- session.py
|
||||||
|
| `-- crud.py
|
||||||
|
|-- models/
|
||||||
|
|-- schemas/
|
||||||
|
`-- tests/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Application Factory
|
||||||
|
|
||||||
|
Use a factory so tests and workers can build the app with controlled settings.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
from app.api.routes import health, users
|
||||||
|
from app.config import settings
|
||||||
|
from app.db.session import close_db, init_db
|
||||||
|
from app.exceptions import register_exception_handlers
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
await init_db()
|
||||||
|
yield
|
||||||
|
await close_db()
|
||||||
|
|
||||||
|
|
||||||
|
def create_app() -> FastAPI:
|
||||||
|
app = FastAPI(
|
||||||
|
title=settings.api_title,
|
||||||
|
version=settings.api_version,
|
||||||
|
lifespan=lifespan,
|
||||||
|
)
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=settings.cors_origins,
|
||||||
|
allow_credentials=bool(settings.cors_origins),
|
||||||
|
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
|
||||||
|
allow_headers=["Authorization", "Content-Type"],
|
||||||
|
)
|
||||||
|
|
||||||
|
register_exception_handlers(app)
|
||||||
|
app.include_router(health.router, prefix="/health", tags=["health"])
|
||||||
|
app.include_router(users.router, prefix="/api/v1/users", tags=["users"])
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not use `allow_origins=["*"]` with `allow_credentials=True`; browsers reject that combination and Starlette disallows it for credentialed requests.
|
||||||
|
|
||||||
|
## Pydantic Schemas
|
||||||
|
|
||||||
|
Keep request, update, and response models separate.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Annotated
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
||||||
|
|
||||||
|
|
||||||
|
class UserBase(BaseModel):
|
||||||
|
email: EmailStr
|
||||||
|
full_name: Annotated[str, Field(min_length=1, max_length=100)]
|
||||||
|
|
||||||
|
|
||||||
|
class UserCreate(UserBase):
|
||||||
|
password: Annotated[str, Field(min_length=12, max_length=128)]
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdate(BaseModel):
|
||||||
|
email: EmailStr | None = None
|
||||||
|
full_name: Annotated[str | None, Field(min_length=1, max_length=100)] = None
|
||||||
|
|
||||||
|
|
||||||
|
class UserResponse(UserBase):
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
id: UUID
|
||||||
|
created_at: datetime
|
||||||
|
updated_at: datetime
|
||||||
|
```
|
||||||
|
|
||||||
|
Response models must never include password hashes, access tokens, refresh tokens, or internal authorization state.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Use dependency injection for request-scoped resources.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from collections.abc import AsyncIterator
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from fastapi import Depends, HTTPException, status
|
||||||
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.core.security import decode_token
|
||||||
|
from app.db.session import session_factory
|
||||||
|
from app.models.user import User
|
||||||
|
|
||||||
|
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
|
||||||
|
|
||||||
|
|
||||||
|
async def get_db() -> AsyncIterator[AsyncSession]:
|
||||||
|
async with session_factory() as session:
|
||||||
|
try:
|
||||||
|
yield session
|
||||||
|
await session.commit()
|
||||||
|
except Exception:
|
||||||
|
await session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
async def get_current_user(
|
||||||
|
token: str = Depends(oauth2_scheme),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> User:
|
||||||
|
payload = decode_token(token)
|
||||||
|
user_id = UUID(payload["sub"])
|
||||||
|
user = await db.get(User, user_id)
|
||||||
|
if user is None:
|
||||||
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
|
||||||
|
return user
|
||||||
|
```
|
||||||
|
|
||||||
|
Avoid creating sessions, clients, or credentials inline inside route handlers.
|
||||||
|
|
||||||
|
## Async Endpoints
|
||||||
|
|
||||||
|
Keep route handlers async when they perform I/O, and use async libraries inside them.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import APIRouter, Depends, Query
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.dependencies import get_current_user, get_db
|
||||||
|
from app.models.user import User
|
||||||
|
from app.schemas.user import UserResponse
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/", response_model=list[UserResponse])
|
||||||
|
async def list_users(
|
||||||
|
limit: int = Query(default=50, ge=1, le=100),
|
||||||
|
offset: int = Query(default=0, ge=0),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
result = await db.execute(
|
||||||
|
select(User).order_by(User.created_at.desc()).limit(limit).offset(offset)
|
||||||
|
)
|
||||||
|
return result.scalars().all()
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `httpx.AsyncClient` for external HTTP calls from async handlers. Do not call `requests` in an async route.
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
Centralize domain exceptions and keep response shapes stable.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, Request
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ApiError(Exception):
|
||||||
|
def __init__(self, status_code: int, code: str, message: str):
|
||||||
|
self.status_code = status_code
|
||||||
|
self.code = code
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
|
||||||
|
def register_exception_handlers(app: FastAPI) -> None:
|
||||||
|
@app.exception_handler(ApiError)
|
||||||
|
async def api_error_handler(request: Request, exc: ApiError):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=exc.status_code,
|
||||||
|
content={"error": {"code": exc.code, "message": exc.message}},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## OpenAPI Customization
|
||||||
|
|
||||||
|
Assign the custom OpenAPI callable to `app.openapi`; do not just call the function once.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.openapi.utils import get_openapi
|
||||||
|
|
||||||
|
|
||||||
|
def install_openapi(app: FastAPI) -> None:
|
||||||
|
def custom_openapi():
|
||||||
|
if app.openapi_schema:
|
||||||
|
return app.openapi_schema
|
||||||
|
app.openapi_schema = get_openapi(
|
||||||
|
title="Service API",
|
||||||
|
version="1.0.0",
|
||||||
|
routes=app.routes,
|
||||||
|
)
|
||||||
|
return app.openapi_schema
|
||||||
|
|
||||||
|
app.openapi = custom_openapi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Override the dependency used by `Depends`, not an internal helper that route handlers never reference.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import pytest
|
||||||
|
from httpx import ASGITransport, AsyncClient
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.dependencies import get_db
|
||||||
|
from app.main import create_app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def client(test_session: AsyncSession):
|
||||||
|
app = create_app()
|
||||||
|
|
||||||
|
async def override_get_db():
|
||||||
|
yield test_session
|
||||||
|
|
||||||
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
|
async with AsyncClient(
|
||||||
|
transport=ASGITransport(app=app),
|
||||||
|
base_url="http://test",
|
||||||
|
) as test_client:
|
||||||
|
yield test_client
|
||||||
|
app.dependency_overrides.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
- Hash passwords with `argon2-cffi`, `bcrypt`, or a current passlib-compatible hasher.
|
||||||
|
- Validate JWT issuer, audience, expiry, and signing algorithm.
|
||||||
|
- Keep CORS origins environment-specific.
|
||||||
|
- Put rate limits on auth and write-heavy endpoints.
|
||||||
|
- Use Pydantic models for all request bodies.
|
||||||
|
- Use ORM parameter binding or SQLAlchemy Core expressions; never build SQL with f-strings.
|
||||||
|
- Redact tokens, authorization headers, cookies, and passwords from logs.
|
||||||
|
- Run dependency audit tooling in CI.
|
||||||
|
|
||||||
|
## Performance Checklist
|
||||||
|
|
||||||
|
- Configure database connection pooling explicitly.
|
||||||
|
- Add pagination to list endpoints.
|
||||||
|
- Watch for N+1 queries and use eager loading intentionally.
|
||||||
|
- Use async HTTP/database clients in async paths.
|
||||||
|
- Add compression only after checking payload size and CPU tradeoffs.
|
||||||
|
- Cache stable expensive reads behind explicit invalidation.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Use these examples as patterns, not as project-wide templates:
|
||||||
|
|
||||||
|
- Application factory: configure middleware and routers once in `create_app`.
|
||||||
|
- Schema split: `UserCreate`, `UserUpdate`, and `UserResponse` have different responsibilities.
|
||||||
|
- Dependency override: tests override `get_db` directly.
|
||||||
|
- OpenAPI customization: assign `app.openapi = custom_openapi`.
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- Agent: `fastapi-reviewer`
|
||||||
|
- Command: `/fastapi-review`
|
||||||
|
- Skill: `python-patterns`
|
||||||
|
- Skill: `python-testing`
|
||||||
|
- Skill: `api-design`
|
||||||
Loading…
x
Reference in New Issue
Block a user