Get the FREE Ultimate OpenClaw Setup Guide →

Fastapi Patterns

Scanned
npx machina-cli add skill ruslan-korneev/python-backend-claude-plugins/fastapi-patterns --openclaw
Files (1)
SKILL.md
5.3 KB

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 — HTTP endpoints, request validation
  • Service — business logic, orchestration
  • Repository — database operations, CRUD operations

Modular Structure

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

BaseRepository with Generics

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

DTOs with Pydantic v2

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

Dependency Injection

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)

Exception Handling

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)

Transaction Management

# 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

Plugin Commands

  • /fastapi:module <name> — create a complete module
  • /fastapi:endpoint <method> <path> <module> — create an endpoint
  • /fastapi:dto <model> — create DTOs from model

Source

git clone https://github.com/ruslan-korneev/python-backend-claude-plugins/blob/master/plugins/fastapi/skills/fastapi-patterns/SKILL.mdView on GitHub

Overview

Provides best practices and patterns for FastAPI projects that use SQLAlchemy 2.0 and dependency-injector. It outlines a layered Router → Service → Repository → Database architecture, DTO-driven data exchange with Pydantic v2, and a modular structure driven by a DI container.

How This Skill Works

The pattern enforces a clean separation of concerns: DTOs map data between layers, a generic BaseRepository handles CRUD with SQLAlchemy, and services orchestrate business logic. Routers validate HTTP requests and delegate to services, while a Dependency Injection container wires dependencies and config.

When to Use It

  • When starting a new FastAPI project or module
  • When you need clear application structure guidance
  • When modeling with SQLAlchemy models and repositories
  • When configuring dependency injection across layers
  • When designing DTOs with Pydantic models

Quick Start

  1. Step 1: Create a modular FastAPI project structure (core, modules, main.py)
  2. Step 2: Implement DTOs with Pydantic v2 and a BaseRepository
  3. Step 3: Configure and wire the DI container, then run the app

Best Practices

  • Enforce Router → Service → Repository → Database layering
  • Use a generic BaseRepository to standardize CRUD across models
  • Define DTOs with Pydantic v2 and ORM-compatible Config
  • Centralize configuration and DI in a dedicated container
  • Handle errors with consistent exception classes and mapping

Example Use Cases

  • A users module with models.py, dto.py, repositories.py, services.py, routers.py
  • A shared BaseRepository used by multiple modules for CRUD
  • DI Container with session_maker and providers for repositories and services
  • Routers.py using dependency injection to obtain services
  • Main.py wiring and application initialization

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers