Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ CI (`.github/workflows/main.yml`) runs `ruff format --check`, `ruff check --no-f
1. Creates a `modern_di.Container` with the `Dependencies` group from `app/ioc.py`.
2. Builds a `FastAPIBootstrapper` from `settings.api_bootstrapper_config`, injecting SQLAlchemy + asyncpg OpenTelemetry instrumentors.
3. `modern_di_fastapi.setup_di(app, container)` wires DI scopes onto the FastAPI app.
4. Includes `app.api.decks.ROUTER` under `/api`.
4. Includes one `ROUTER` per resource (`app.api.decks.ROUTER`, `app.api.cards.ROUTER`) under `/api` via `include_routers`. Add a new resource by creating `app/api/<name>.py` with its own `APIRouter`-based `ROUTER` and registering it there.
5. Registers `DuplicateKeyError` → 422 handler from `app/exceptions.py`.

### DI scopes (modern-di)
Expand Down
48 changes: 48 additions & 0 deletions app/api/cards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import typing

import fastapi
from modern_di_fastapi import FromDI

from app import models, schemas
from app.repositories import CardsRepository


ROUTER: typing.Final = fastapi.APIRouter()


@ROUTER.get("/decks/{deck_id}/cards/")
async def list_cards(
deck_id: int,
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Cards:
objects = await cards_repository.list_for_deck(deck_id)
return schemas.Cards.from_models(objects)


@ROUTER.get("/cards/{card_id}/")
async def get_card(
card_id: int,
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Card:
instance = await cards_repository.get_one(models.Card.id == card_id)
return schemas.Card.model_validate(instance)


@ROUTER.post("/decks/{deck_id}/cards/")
async def create_cards(
deck_id: int,
data: list[schemas.CardCreate],
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Cards:
objects = await cards_repository.add_cards(deck_id, data)
return schemas.Cards.from_models(objects)


@ROUTER.put("/decks/{deck_id}/cards/")
async def update_cards(
deck_id: int,
data: list[schemas.Card],
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Cards:
objects = await cards_repository.upsert_cards(deck_id, data)
return schemas.Cards.from_models(objects)
42 changes: 2 additions & 40 deletions app/api/decks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import fastapi
from modern_di_fastapi import FromDI

from app import models, schemas
from app.repositories import CardsRepository, DecksRepository
from app import schemas
from app.repositories import DecksRepository


ROUTER: typing.Final = fastapi.APIRouter()
Expand Down Expand Up @@ -44,41 +44,3 @@ async def create_deck(
) -> schemas.Deck:
instance = await decks_repository.create(data.model_dump())
return schemas.Deck.model_validate(instance)


@ROUTER.get("/decks/{deck_id}/cards/")
async def list_cards(
deck_id: int,
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Cards:
objects = await cards_repository.list_for_deck(deck_id)
return schemas.Cards.from_models(objects)


@ROUTER.get("/cards/{card_id}/")
async def get_card(
card_id: int,
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Card:
instance = await cards_repository.get_one(models.Card.id == card_id)
return schemas.Card.model_validate(instance)


@ROUTER.post("/decks/{deck_id}/cards/")
async def create_cards(
deck_id: int,
data: list[schemas.CardCreate],
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Cards:
objects = await cards_repository.add_cards(deck_id, data)
return schemas.Cards.from_models(objects)


@ROUTER.put("/decks/{deck_id}/cards/")
async def update_cards(
deck_id: int,
data: list[schemas.Card],
cards_repository: CardsRepository = FromDI(CardsRepository),
) -> schemas.Cards:
objects = await cards_repository.upsert_cards(deck_id, data)
return schemas.Cards.from_models(objects)
5 changes: 3 additions & 2 deletions app/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

from app import exceptions, ioc
from app.api.decks import ROUTER
from app.api import cards, decks
from app.settings import settings


Expand All @@ -18,7 +18,8 @@


def include_routers(app: fastapi.FastAPI) -> None:
app.include_router(ROUTER, prefix="/api")
app.include_router(decks.ROUTER, prefix="/api")
app.include_router(cards.ROUTER, prefix="/api")


def build_app() -> fastapi.FastAPI:
Expand Down