| name | backend-fastapi-python |
| description | Use this skill for any Python backend work in this project: building FastAPI endpoints, writing service functions, defining Pydantic/SQLModel schemas, running Alembic migrations, or debugging 422 errors. Essential for authentication and authorization patterns — setting up get_current_user, is_superuser checks, admin-only guards, role-based access, and dependency injection chains like Depends(). Also covers middleware, background tasks, async SQLAlchemy sessions, ORM relationship loading, and request/response design. Activate whenever the question involves Python API code, FastAPI patterns, or backend architecture in this codebase. Not for frontend, Docker, CI/CD, or infrastructure. |
Backend FastAPI Python
Project-specific conventions for FastAPI with SQLModel, pydantic-settings, and async SQLAlchemy.
Architecture Decisions
- Services are stateless functions — Not classes. First param is
db: AsyncSession.
- Generic response wrapper — Always use
ApiResponse[T] for consistency.
- Dependencies chain —
get_current_user -> require_auth -> require_admin.
- Module-scoped config — Each module can have its own
{module}_config.py.
- Error codes for frontend —
AppException(status, message, error_code).
Gotchas
- SQLModel
Relationship() fields are NOT included in API responses by default. You must explicitly add them to model_config or use a separate response schema with those fields.
AsyncSession.refresh() does not load relationships. After commit, re-query with .options(selectinload(...)) if you need related objects.
- Pydantic V2 uses
model_validator not validator. The @validator decorator is V1 and will break silently or raise deprecation warnings.
Depends() in FastAPI creates a NEW instance per request — don't store state in dependency return values expecting it to persist.
- Background tasks (
BackgroundTasks) run AFTER the response is sent. If they fail, the client already got a 200. Use proper task queues (Celery, ARQ) for anything that must not silently fail.
- Alembic
--autogenerate misses: table renames (generates drop+create), index changes on existing columns, and Enum type modifications in PostgreSQL. Always review generated migrations.
async def endpoints block the event loop if you call sync I/O inside them. Use run_in_executor for sync libraries or define the endpoint as def (FastAPI runs sync endpoints in a threadpool).
HTTPException from FastAPI and HTTPException from Starlette are different classes. Importing the wrong one causes middleware to miss exception handlers.
- SQLAlchemy's
lazy="selectin" on relationships causes N+1 queries in async sessions. Use explicit selectinload() in queries instead.
Optional[str] = None in query params makes the field optional. str = None also works but loses type information — prefer the explicit Optional form.
- When using
response_model, FastAPI filters OUT any fields not in the model. If your response is missing data, check that the response model includes all fields, not just the ORM model.
References