plugins/fastapi/skills/fastapi-patterns/SKILL.md
# FastAPI Patterns Best practices and patterns for FastAPI projects with SQLAlchemy 2.0 and dependency-injector. ## Triggers Use this skill when the user: - Creates a new FastAPI project or module - Asks about FastAPI application structure - Works with SQLAlchemy models and repositories - Configures Dependency Injection - Creates DTOs (Pydantic models) ## Layer Architecture ``` Router → Service → Repository → Database ↓ ↓ ↓ DTO DTO Model/DTO ``` - **Router** —
npx skillsauth add ruslan-korneev/claude-plugins plugins/fastapi/skills/fastapi-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Best practices and patterns for FastAPI projects with SQLAlchemy 2.0 and dependency-injector.
Use this skill when the user:
Router → Service → Repository → Database
↓ ↓ ↓
DTO DTO Model/DTO
src/
├── core/
│ ├── config.py # Settings (pydantic-settings)
│ ├── database.py # Engine, Base, session
│ ├── container.py # DI Container
│ ├── dependencies.py # FastAPI dependencies
│ ├── exceptions.py # Custom exceptions
│ └── repositories.py # BaseRepository
├── modules/
│ ├── users/
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── dto.py
│ │ ├── repositories.py
│ │ ├── services.py
│ │ └── routers.py
│ └── orders/
│ └── ...
└── main.py
More details: ${CLAUDE_PLUGIN_ROOT}/skills/fastapi-patterns/references/repository.md
from typing import Generic, TypeVar
from collections.abc import Sequence
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
ModelT = TypeVar("ModelT")
CreateDTOT = TypeVar("CreateDTOT")
ReadDTOT = TypeVar("ReadDTOT")
class BaseRepository(Generic[ModelT, CreateDTOT, ReadDTOT]):
"""Base repository with generic CRUD operations."""
def __init__(self, session: AsyncSession, model: type[ModelT]) -> None:
self._session = session
self._model = model
async def get_all(self) -> Sequence[ReadDTOT]:
result = await self._session.execute(select(self._model))
return result.scalars().all()
async def save(self, data: ModelT | CreateDTOT) -> ReadDTOT:
if isinstance(data, self._model):
entity = data
else:
entity = self._model(**data.model_dump())
self._session.add(entity)
await self._session.flush()
return entity
More details: ${CLAUDE_PLUGIN_ROOT}/skills/fastapi-patterns/references/dto.md
from pydantic import BaseModel, ConfigDict
class BaseDTO(BaseModel):
"""Base DTO with ORM mode."""
model_config = ConfigDict(
from_attributes=True,
populate_by_name=True,
str_strip_whitespace=True,
)
class UserCreateDTO(BaseDTO):
email: str
name: str
class UserReadDTO(BaseDTO):
id: int
email: str
name: str
More details: ${CLAUDE_PLUGIN_ROOT}/skills/fastapi-patterns/references/di.md
# container.py
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
session_maker = providers.Singleton(
async_sessionmaker,
bind=engine,
)
user_repository = providers.Factory(
UserRepository,
session=session_maker,
)
user_service = providers.Factory(
UserService,
repository=user_repository,
)
# routers.py
from dependency_injector.wiring import inject, Provide
@router.get("/users/{id}")
@inject
async def get_user(
id: int,
service: Annotated[UserService, Depends(Provide[Container.user_service])],
) -> UserReadDTO:
return await service.get_by_id(id)
More details: ${CLAUDE_PLUGIN_ROOT}/skills/fastapi-patterns/references/exceptions.md
# exceptions.py
class AppError(Exception):
"""Base application error."""
status_code: int = 500
detail: str = "Internal server error"
def __init__(self, detail: str | None = None) -> None:
self.detail = detail or self.detail
class NotFoundError(AppError):
status_code = 404
detail = "Resource not found"
class ConflictError(AppError):
status_code = 409
detail = "Resource already exists"
# handlers.py
async def app_exception_handler(request: Request, exc: AppError) -> JSONResponse:
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail},
)
# main.py
app.add_exception_handler(AppError, app_exception_handler)
# Via session_maker context manager
async def create_order(self, data: OrderCreateDTO) -> OrderReadDTO:
async with self._session_maker() as session:
order = Order(**data.model_dump())
session.add(order)
await session.commit()
return OrderReadDTO.model_validate(order)
# Or via repository
async def create_with_items(self, data: OrderCreateDTO) -> OrderReadDTO:
order = await self._order_repo.save(data)
for item in data.items:
await self._item_repo.save(OrderItem(order_id=order.id, **item))
await self._session.commit()
return order
/fastapi:module <name> — create a complete module/fastapi:endpoint <method> <path> <module> — create an endpoint/fastapi:dto <model> — create DTOs from modeltools
# Code Review Skill This skill should be used when the user asks for "code review", "review my changes", "review this PR", "check my code", "pre-merge review", "review diff", or mentions reviewing code quality, implementation correctness, or preparing changes for merge. ## Overview Code review following the **Review Pyramid** methodology — a prioritized approach that focuses on what matters most. ## The Review Pyramid ``` ▲ /|\ 5. Code Style (Nit) ← Least important,
tools
# Architecture Patterns System-level architecture patterns for Python backend projects (FastAPI + SQLAlchemy 2.0). ## Triggers Use this skill when the user: - Designs a new system or major feature - Asks about project structure or module boundaries - Makes data modeling decisions - Designs API contracts - Evaluates architectural trade-offs - Creates or reviews Architecture Decision Records (ADR) - Needs to modernize legacy code ## Abstraction Levels | Plugin | Level | Focus | |--------|----
tools
# Python Typing Patterns Python type annotation patterns without `type: ignore`. Always the correct solution. ## Triggers Use this skill when the user: - Gets mypy/pyright errors - Asks about Python type annotations - Wants to add type hints - Works with generics, protocols, TypeVar ## Main Principle: NEVER type: ignore Every type error has a correct solution. `type: ignore` is: - Masking potential bugs - Disabling type checking - Technical debt More details: `${CLAUDE_PLUGIN_ROOT}/skills/
tools
# Ruff Patterns Knowledge about the ruff linter and patterns for solving errors. **ZERO noqa policy** — always look for the proper solution. ## Triggers Use this skill when the user: - Asks "how to configure ruff" - Gets a ruff error and wants to understand how to fix it - Wants to migrate from black/isort/flake8/pylint - Asks about a specific rule (E501, F401, etc.) ## Main Principle: NEVER USE NOQA **Every ruff error has a proper solution.** Using `# noqa` is: - Hiding the problem, not so