<< All versions
Skill v1.0.0
currentAutomated scan100/100izangi2714/claude-code-python-stack/fastapi-patterns
──Details
PublishedSeptember 28, 2026 at 12:40 PM
Content Hashsha256:7862dc81566bf12f...
Git SHA58228285db35
──Files
Files (1 file, 12.3 KB)
SKILL.md12.3 KBactive
SKILL.md · 440 lines · 12.3 KB
version: "1.0.0" name: fastapi-patterns description: FastAPI patterns for building production-grade APIs — routing, Pydantic models, dependency injection, middleware, async patterns, WebSockets, and background tasks. origin: custom
FastAPI Development Patterns
Production-grade FastAPI architecture patterns for high-performance async APIs.
When to Activate
- Building FastAPI web applications
- Designing REST or GraphQL APIs with FastAPI
- Working with async Python and ASGI
- Setting up dependency injection
- Implementing WebSockets or background tasks
Project Structure
myproject/├── src/│ └── app/│ ├── __init__.py│ ├── main.py # FastAPI app factory│ ├── config.py # Settings (Pydantic BaseSettings)│ ├── dependencies.py # Shared dependencies│ ├── middleware.py # Custom middleware│ ├── api/│ │ ├── __init__.py│ │ ├── v1/│ │ │ ├── __init__.py│ │ │ ├── router.py # APIRouter aggregator│ │ │ ├── users.py│ │ │ ├── products.py│ │ │ └── auth.py│ │ └── deps.py # API-specific dependencies│ ├── models/ # SQLAlchemy models│ │ ├── __init__.py│ │ ├── user.py│ │ └── product.py│ ├── schemas/ # Pydantic schemas│ │ ├── __init__.py│ │ ├── user.py│ │ └── product.py│ ├── services/ # Business logic│ │ ├── __init__.py│ │ ├── user_service.py│ │ └── product_service.py│ ├── repositories/ # Data access│ │ ├── __init__.py│ │ └── user_repo.py│ └── core/│ ├── __init__.py│ ├── security.py # JWT, hashing│ ├── exceptions.py # Custom exceptions│ └── database.py # DB session management├── tests/│ ├── conftest.py│ ├── test_users.py│ └── test_products.py├── alembic/│ └── versions/├── alembic.ini├── pyproject.toml├── Dockerfile└── docker-compose.yml
App Factory
python
from fastapi import FastAPIfrom contextlib import asynccontextmanagerfrom app.api.v1.router import api_routerfrom app.config import settingsfrom app.core.database import engine@asynccontextmanagerasync def lifespan(app: FastAPI):# Startup# await init_db()yield# Shutdownawait engine.dispose()def create_app() -> FastAPI:app = FastAPI(title=settings.PROJECT_NAME,version=settings.VERSION,docs_url="/docs" if settings.DEBUG else None,lifespan=lifespan,)app.include_router(api_router, prefix="/api/v1")return appapp = create_app()
Configuration with Pydantic Settings
python
from pydantic_settings import BaseSettingsfrom functools import lru_cacheclass Settings(BaseSettings):PROJECT_NAME: str = "MyAPI"VERSION: str = "1.0.0"DEBUG: bool = FalseDATABASE_URL: strREDIS_URL: str = "redis://localhost:6379/0"SECRET_KEY: strACCESS_TOKEN_EXPIRE_MINUTES: int = 30ALGORITHM: str = "HS256"CORS_ORIGINS: list[str] = ["http://localhost:3000"]model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}@lru_cachedef get_settings() -> Settings:return Settings()settings = get_settings()
Dependency Injection
python
from fastapi import Dependsfrom sqlalchemy.ext.asyncio import AsyncSessionfrom app.core.database import get_db_session# Database session dependencyasync def get_db() -> AsyncGenerator[AsyncSession, None]:async with get_db_session() as session:yield session# Current user dependencyasync def get_current_user(token: str = Depends(oauth2_scheme),db: AsyncSession = Depends(get_db),) -> User:payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])user_id = payload.get("sub")if not user_id:raise HTTPException(status_code=401, detail="Invalid token")user = await db.get(User, user_id)if not user:raise HTTPException(status_code=401, detail="User not found")return user# Service dependencyasync def get_user_service(db: AsyncSession = Depends(get_db)) -> UserService:return UserService(db)
Request/Response Schemas
python
from pydantic import BaseModel, EmailStr, Fieldfrom datetime import datetimeclass UserCreate(BaseModel):email: EmailStrpassword: str = Field(min_length=8, max_length=128)name: str = Field(min_length=1, max_length=255)class UserResponse(BaseModel):id: intemail: strname: strcreated_at: datetimemodel_config = {"from_attributes": True}class UserUpdate(BaseModel):name: str | None = Noneemail: EmailStr | None = Noneclass PaginatedResponse(BaseModel):items: list[UserResponse]total: intpage: intper_page: intpages: int
Router Patterns
python
from fastapi import APIRouter, Depends, HTTPException, Query, statusfrom app.schemas.user import UserCreate, UserResponse, UserUpdate, PaginatedResponsefrom app.services.user_service import UserServicefrom app.api.deps import get_current_user, get_user_servicerouter = APIRouter(prefix="/users", tags=["users"])@router.get("/", response_model=PaginatedResponse)async def list_users(page: int = Query(1, ge=1),per_page: int = Query(20, ge=1, le=100),service: UserService = Depends(get_user_service),):return await service.list_users(page=page, per_page=per_page)@router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)async def create_user(data: UserCreate,service: UserService = Depends(get_user_service),):return await service.create_user(data)@router.get("/me", response_model=UserResponse)async def get_me(current_user: User = Depends(get_current_user)):return current_user@router.get("/{user_id}", response_model=UserResponse)async def get_user(user_id: int,service: UserService = Depends(get_user_service),):user = await service.get_user(user_id)if not user:raise HTTPException(status_code=404, detail="User not found")return user@router.patch("/{user_id}", response_model=UserResponse)async def update_user(user_id: int,data: UserUpdate,current_user: User = Depends(get_current_user),service: UserService = Depends(get_user_service),):if current_user.id != user_id:raise HTTPException(status_code=403, detail="Not authorized")return await service.update_user(user_id, data)@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)async def delete_user(user_id: int,current_user: User = Depends(get_current_user),service: UserService = Depends(get_user_service),):await service.delete_user(user_id)
Error Handling
python
from fastapi import Requestfrom fastapi.responses import JSONResponseclass AppException(Exception):def __init__(self, status_code: int, detail: str, error_code: str | None = None):self.status_code = status_codeself.detail = detailself.error_code = error_codeclass NotFoundError(AppException):def __init__(self, resource: str, id: str | int):super().__init__(404, f"{resource} with id {id} not found", "NOT_FOUND")class ConflictError(AppException):def __init__(self, detail: str):super().__init__(409, detail, "CONFLICT")# Register exception handler@app.exception_handler(AppException)async def app_exception_handler(request: Request, exc: AppException):return JSONResponse(status_code=exc.status_code,content={"error": exc.error_code or "ERROR","detail": exc.detail,},)
Middleware
python
import timeimport loggingfrom starlette.middleware.base import BaseHTTPMiddlewarelogger = logging.getLogger(__name__)class RequestLoggingMiddleware(BaseHTTPMiddleware):async def dispatch(self, request, call_next):start = time.perf_counter()response = await call_next(request)duration = time.perf_counter() - startlogger.info(f"{request.method} {request.url.path} "f"status={response.status_code} duration={duration:.3f}s")return response# CORS setupfrom fastapi.middleware.cors import CORSMiddlewareapp.add_middleware(CORSMiddleware,allow_origins=settings.CORS_ORIGINS,allow_credentials=True,allow_methods=["*"],allow_headers=["*"],)
Background Tasks
python
from fastapi import BackgroundTasks@router.post("/users/", response_model=UserResponse, status_code=201)async def create_user(data: UserCreate,background_tasks: BackgroundTasks,service: UserService = Depends(get_user_service),):user = await service.create_user(data)background_tasks.add_task(send_welcome_email, user.email, user.name)return userasync def send_welcome_email(email: str, name: str):# Send email asynchronously...
WebSocket
python
from fastapi import WebSocket, WebSocketDisconnectclass ConnectionManager:def __init__(self):self.active: list[WebSocket] = []async def connect(self, ws: WebSocket):await ws.accept()self.active.append(ws)def disconnect(self, ws: WebSocket):self.active.remove(ws)async def broadcast(self, message: str):for ws in self.active:await ws.send_text(message)manager = ConnectionManager()@app.websocket("/ws")async def websocket_endpoint(ws: WebSocket):await manager.connect(ws)try:while True:data = await ws.receive_text()await manager.broadcast(f"Message: {data}")except WebSocketDisconnect:manager.disconnect(ws)
Testing
python
import pytestfrom httpx import AsyncClient, ASGITransportfrom app.main import app@pytest.fixtureasync def client():async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:yield ac@pytest.mark.asyncioasync def test_create_user(client: AsyncClient):response = await client.post("/api/v1/users/", json={"email": "test@example.com","password": "securepass123","name": "Test User",})assert response.status_code == 201assert response.json()["email"] == "test@example.com"@pytest.mark.asyncioasync def test_list_users(client: AsyncClient):response = await client.get("/api/v1/users/")assert response.status_code == 200assert "items" in response.json()
Security
python
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestFormfrom jose import JWTError, jwtfrom passlib.context import CryptContextfrom datetime import datetime, timedeltapwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:to_encode = data.copy()expire = datetime.utcnow() + (expires_delta or timedelta(minutes=30))to_encode.update({"exp": expire})return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)def verify_password(plain: str, hashed: str) -> bool:return pwd_context.verify(plain, hashed)def hash_password(password: str) -> str:return pwd_context.hash(password)
Quick Reference
| Pattern | Description | |
|---|---|---|
| App factory | create_app() with lifespan | |
| Pydantic Settings | Config from env vars | |
| Depends() | Dependency injection | |
| APIRouter | Modular route organization | |
| BackgroundTasks | Fire-and-forget tasks | |
| WebSocket | Real-time communication | |
| httpx + AsyncClient | Async test client | |
| OAuth2 + JWT | Token-based auth |
Remember: FastAPI is async-first. Use async def for I/O-bound endpoints. Avoid blocking calls in async routes -- use run_in_executor or background tasks for CPU-bound work.