---
name: ecc-fastapi-patterns
description: FastAPI patterns for async APIs, dependency injection, Pydantic request and response models, OpenAPI docs, tests, security, and production readiness.
---

# 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
- Adding auth, OpenAPI docs, tests, or deployment settings

## Project Layout
```
app/
├── main.py              # app construction, middleware, exception handlers
├── config.py            # settings
├── dependencies.py      # DB, auth, pagination dependencies
├── exceptions.py        # centralized error handling
├── api/routes/          # users.py, health.py
├── core/                # security.py, middleware.py
├── db/                  # session.py, crud.py
├── models/
├── schemas/
└── tests/
```

## Application Factory
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from app.config import settings

@asynccontextmanager
async def lifespan(app: FastAPI):
    await init_db()
    yield
    await close_db()

def create_app() -> FastAPI:
    app = FastAPI(title=settings.api_title, lifespan=lifespan)
    app.add_middleware(CORSMiddleware, allow_origins=settings.cors_origins, ...)
    app.include_router(users.router, prefix="/api/v1/users")
    return app
```

## Pydantic Schemas
Keep request, update, and response models separate. Response models must NEVER include password hashes, tokens, or internal state.

## Dependencies
Use DI for request-scoped resources — DB sessions, current user, pagination.

## Async Endpoints
Use async for I/O, `httpx.AsyncClient` for HTTP calls, async SQLAlchemy sessions.

## Testing
Override `app.dependency_overrides` directly — not internal helpers.

## Security Checklist
- Hash passwords with argon2/bcrypt
- Validate JWT issuer, audience, expiry
- Rate-limit auth endpoints
- Always use Pydantic for request bodies
- Never f-string SQL
- Redact secrets from logs
