fastapi-patterns
FastAPI architecture patterns — async endpoints, Pydantic models, dependency injection, OpenAPI, background tasks, and testing with pytest + HTTPX.
Best use case
fastapi-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
FastAPI architecture patterns — async endpoints, Pydantic models, dependency injection, OpenAPI, background tasks, and testing with pytest + HTTPX.
Teams using fastapi-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/fastapi-patterns/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How fastapi-patterns Compares
| Feature / Agent | fastapi-patterns | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
FastAPI architecture patterns — async endpoints, Pydantic models, dependency injection, OpenAPI, background tasks, and testing with pytest + HTTPX.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# FastAPI Patterns
Modern Python API development with FastAPI, Pydantic v2, and async/await.
## When to Activate
- Building or reviewing FastAPI API endpoints
- Designing Pydantic request/response schemas
- Implementing dependency injection with `Depends()`
- Writing async database queries (SQLAlchemy async, Motor)
- Setting up FastAPI testing with pytest + HTTPX
- Structuring a FastAPI project (routers, services, repositories)
- Migrating a Flask or Django REST Framework app to FastAPI
- Adding background tasks or lifespan event handlers to an existing FastAPI application
- Configuring global exception handlers to return RFC 7807 Problem Details responses
---
## Project Structure
```
app/
├── main.py # FastAPI app creation, middleware, lifespan
├── api/
│ ├── v1/
│ │ ├── router.py # APIRouter aggregator
│ │ ├── users.py # User endpoints
│ │ └── items.py # Item endpoints
├── core/
│ ├── config.py # Settings (pydantic-settings)
│ └── security.py # JWT, password hashing
├── models/
│ └── db.py # SQLAlchemy ORM models
├── schemas/
│ ├── user.py # Pydantic request/response schemas
│ └── item.py
├── services/
│ └── user_service.py # Business logic (no framework deps)
├── repositories/
│ └── user_repo.py # Data access (SQLAlchemy queries)
├── dependencies/
│ └── auth.py # Shared Depends() functions
└── tests/
├── conftest.py
└── api/test_users.py
```
---
## App Setup
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.api.v1 import router as v1_router
from app.core.config import settings
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
await db.connect()
yield
# Shutdown
await db.disconnect()
app = FastAPI(
title=settings.app_name,
version="1.0.0",
lifespan=lifespan,
)
app.include_router(v1_router, prefix="/api/v1")
```
---
## Pydantic Schemas
```python
from pydantic import BaseModel, EmailStr, field_validator
from datetime import datetime
from uuid import UUID
class UserCreate(BaseModel):
email: EmailStr
name: str
password: str
@field_validator("name")
@classmethod
def name_must_not_be_blank(cls, v: str) -> str:
if not v.strip():
raise ValueError("name must not be blank")
return v.strip()
class UserResponse(BaseModel):
id: UUID
email: EmailStr
name: str
created_at: datetime
model_config = {"from_attributes": True} # ORM mode
```
---
## Async Endpoints
```python
from fastapi import APIRouter, Depends, HTTPException, status
from app.schemas.user import UserCreate, UserResponse
from app.services.user_service import UserService
from app.dependencies.auth import get_current_user
router = APIRouter(prefix="/users", tags=["users"])
@router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(
payload: UserCreate,
service: UserService = Depends(),
) -> UserResponse:
return await service.create(payload)
@router.get("/me", response_model=UserResponse)
async def get_me(
current_user: UserResponse = Depends(get_current_user),
) -> UserResponse:
return current_user
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: UUID, service: UserService = Depends()) -> UserResponse:
user = await service.get_by_id(user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
```
---
## Dependency Injection
```python
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from app.core.security import decode_jwt
from app.repositories.user_repo import UserRepository
bearer = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(bearer),
repo: UserRepository = Depends(),
) -> UserResponse:
payload = decode_jwt(credentials.credentials)
if not payload:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
user = await repo.find_by_id(payload["sub"])
if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
return user
```
---
## Configuration (pydantic-settings)
```python
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "My API"
database_url: str
secret_key: str
access_token_expire_minutes: int = 30
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
settings = Settings()
```
---
## Background Tasks
```python
from fastapi import BackgroundTasks
async def send_welcome_email(email: str) -> None:
# heavy I/O — runs after response is sent
await email_client.send(to=email, subject="Welcome!")
@router.post("/", response_model=UserResponse, status_code=201)
async def create_user(
payload: UserCreate,
background_tasks: BackgroundTasks,
service: UserService = Depends(),
) -> UserResponse:
user = await service.create(payload)
background_tasks.add_task(send_welcome_email, user.email)
return user
```
---
## Error Handling
```python
from fastapi import Request
from fastapi.responses import JSONResponse
class DomainError(Exception):
def __init__(self, message: str, status_code: int = 400):
self.message = message
self.status_code = status_code
@app.exception_handler(DomainError)
async def domain_error_handler(request: Request, exc: DomainError) -> JSONResponse:
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.message},
)
```
---
## Testing
```python
# conftest.py
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
@pytest.fixture
async def client() -> AsyncClient:
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
yield ac
# test_users.py
@pytest.mark.asyncio
async def test_create_user(client: AsyncClient) -> None:
response = await client.post(
"/api/v1/users/",
json={"email": "test@example.com", "name": "Test User", "password": "secret"},
)
assert response.status_code == 201
data = response.json()
assert data["email"] == "test@example.com"
@pytest.mark.asyncio
async def test_get_user_not_found(client: AsyncClient) -> None:
response = await client.get("/api/v1/users/00000000-0000-0000-0000-000000000000")
assert response.status_code == 404
```
Run tests:
```bash
pip install pytest pytest-asyncio httpx
pytest -v --asyncio-mode=auto
```
---
## Key Rules
1. **Pydantic v2** — use `model_config`, `@field_validator`, `model_validate()` (not v1 API)
2. **Async all the way** — use `async def` for endpoints; avoid blocking I/O in async context
3. **`response_model`** — always declare to control serialization and OpenAPI schema
4. **`Depends()` for DI** — inject services, repos, auth via Depends; avoid global state
5. **Lifespan** — use `@asynccontextmanager lifespan` instead of deprecated `@app.on_event`
6. **Status codes** — set `status_code` explicitly on `@router.post()`, not inside the handler
7. **No business logic in endpoints** — delegate to service classes
## Related Skills
- `python-patterns` — Pythonic idioms, PEP 8, type hints
- `python-testing` — pytest fixtures, mocking, coverage
- `ddd-python` — Domain-Driven Design for Python (entities, repositories, domain events)
- `api-design` — REST API design, versioning, error formats
- `postgres-patterns` — PostgreSQL patterns for the data layerRelated Skills
zero-trust-patterns
Zero-Trust security patterns — mTLS between microservices (Istio/SPIFFE), SPIRE workload identity, OPA/Envoy authorization, NetworkPolicy default-deny-all, short-lived credentials, service mesh security, and Kubernetes RBAC hardening.
webrtc-patterns
WebRTC patterns — peer connection setup, ICE/STUN/TURN configuration, signaling server design, SFU vs mesh topology, screen sharing, media track management, and reconnect/ICE restart handling.
webhook-patterns
Webhook patterns for receiving, verifying (HMAC), and idempotently processing third-party events. Covers Stripe, GitHub, and generic webhook patterns, delivery guarantees, retry handling, and testing.
wasm-patterns
WebAssembly patterns: wasm-pack, wasm-bindgen (JS↔Wasm interop), WASI, Component Model, wasm-opt, Rust-to-WASM compilation, JS integration (web workers, streaming instantiation), and production deployment (CDN, Content-Type headers).
ux-micro-patterns
UX micro-patterns for every product state: Empty States, Loading States (skeleton screens, spinners, optimistic UI), Error States, Success States, Confirmation Dialogs, Onboarding Flows, and Progressive Disclosure. These patterns apply to every feature — done wrong, they're the biggest source of user confusion.
typescript-patterns
TypeScript patterns — type system best practices, strict mode, utility types, generics, discriminated unions, error handling with Result types, and module organization. Core patterns for production TypeScript.
typescript-patterns-advanced
Advanced TypeScript — mapped types, template literal types, conditional types, infer, type guards, decorators, async patterns, testing with Vitest/Jest, and performance. Extends typescript-patterns.
typescript-monorepo-patterns
TypeScript monorepo patterns with Turborepo + pnpm workspaces. Covers package structure, shared configs, task pipeline caching, build orchestration, and publishing strategy.
terraform-patterns
Infrastructure as Code with Terraform — project structure, remote state, modules, workspace strategy, AWS/GCP patterns, CI/CD integration, and security hardening. The standard for managing production infrastructure.
swiftui-patterns
SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS/macOS UI best practices.
swift-patterns
Core Swift patterns — value vs reference types, protocols, generics, optionals, Result, error handling, Codable, and module organization. Foundation for all Swift development.
swift-patterns-advanced
Advanced Swift patterns — property wrappers, result builders, Combine basics, opaque & existential types, macro system, advanced generics, and performance optimization. Extends swift-patterns.