Enforces FastAPI conventions including route organization, dependency injection, Pydantic model design, async patterns, and OpenAPI documentation standards.
## FastAPI Project Rules
### Project Structure
- `app/main.py` — FastAPI app factory, lifespan, middleware registration
- `app/routers/` — one router per domain (e.g., `users.py`, `items.py`)
- `app/models/` — SQLAlchemy models or ODM documents
- `app/schemas/` — Pydantic request/response schemas
- `app/services/` — business logic, called from routers
- `app/dependencies/` — reusable `Depends()` factories
- `app/core/` — config, database session, security utilities
### Routers & Routes
- Use `APIRouter` with `prefix`, `tags`, and `responses` for every domain
- Include routers in `main.py` via `app.include_router()`
- Annotate every route with `response_model` for automatic serialization and OpenAPI docs
- Use `status_code` explicitly: `201` for create, `204` for delete, `200` for others
- Use `response_model_exclude_unset=True` when returning partial updates
- Raise `HTTPException` with specific `status_code` and `detail` for error responses
### Pydantic Schemas
- Separate schemas: `[Resource]Create`, `[Resource]Update`, `[Resource]Response`
- `Response` schemas must not expose sensitive fields (passwords, internal IDs)
- Use `model_config = ConfigDict(from_attributes=True)` for ORM model compatibility
- Validate input with field validators using `@field_validator` and `@model_validator`
- Use `Annotated[str, Field(min_length=1, max_length=255)]` for constrained fields
- Use `SecretStr` for password fields to prevent accidental logging
### Dependency Injection
- Define all reusable logic as dependency functions injected via `Depends()`
- Database session dependency: `def get_db() -> Generator[Session, None, None]`
- Authentication dependency: `get_current_user()` returns the authenticated user or raises 401
- Use `Depends(get_current_user)` in route signature, not in the function body
- Chain dependencies: `get_current_active_user` can depend on `get_current_user`
- Use `Security(get_current_user, scopes=["items:read"])` for scope-based auth
### Async Patterns
- Use `async def` for routes that perform I/O (database, HTTP, file operations)
- Use `def` (sync) only for CPU-bound routes; FastAPI runs them in a thread pool
- Use `asyncpg` or `databases` for async PostgreSQL; `motor` for async MongoDB
- Never call blocking I/O (e.g., `requests.get()`, `time.sleep()`) inside `async def` routes
- Use `asyncio.gather()` for concurrent async operations within a single request
### Lifespan & Startup
- Use `@asynccontextmanager` lifespan function for startup/shutdown logic (database pools, ML models)
- Replace deprecated `@app.on_event("startup")` with the lifespan approach
- Initialize connection pools at startup; close them at shutdown
### OpenAPI Documentation
- Add `summary` and `description` to every route for auto-generated docs
- Use `openapi_extra` for additional schema annotations not expressible in Pydantic
- Tag routes consistently — tags map to sections in `/docs`
- Document all possible error responses in `responses` dict of `APIRouter`
### Error Handling
- Register a global `exception_handler` for `RequestValidationError` to standardize 422 format
- Create custom exception classes (e.g., `ItemNotFoundError`) and register handlers
- Never return raw tracebacks to clients — log internally, return sanitized messages
### Security
- Use `OAuth2PasswordBearer` for token-based auth; always validate token expiry
- Hash passwords with `passlib[bcrypt]` — never store plain text
- Use HTTPS-only cookies with `httponly=True, secure=True, samesite="lax"` for session tokens
- Rate limit sensitive endpoints (login, register) with `slowapi` or a proxy
### Anti-patterns
- Do not put database queries directly in route functions — use service/repository layer
- Do not use global mutable state for request-scoped data — use `Depends()`
- Do not return SQLAlchemy model objects directly — always convert to Pydantic schema
- Do not ignore the `response_model` parameter — untyped responses break OpenAPI docs