Implements a use case's backend: FastAPI router, Pydantic request/response schemas, service/domain layer, and SQLAlchemy models/queries. Use when the user asks to "implement the backend for UC-xxx", "build the API endpoint", "add a FastAPI route", "write the service layer", or mentions FastAPI implementation, Pydantic schemas, or SQLAlchemy queries for a specific use case.
77
96%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Implement the backend for use case $ARGUMENTS using FastAPI, Pydantic, and SQLAlchemy, following the three-layer
split in docs/guidelines/architecture.md §2.3: thin routers, Pydantic for contract validation only, business rules
in a service/domain layer. Don't create tests here — use /pytest-test. Don't create or alter migrations here — use
/alembic-migration first if the schema isn't ready.
LIKE — no extra code needed); duplicate pet-name detection (UC-007/UC-008) is
case-insensitive and depends on the LOWER()/citext constraint from the migration — query it consistently with
how it was constrained, don't re-derive the rule from scratch (architecture.md §2.4).offset/limit, defaulting to 20
and capped at 100 server-side regardless of what the client requests (architecture.md §2.2, requirements.md
NFR-001) — don't return an unbounded result set.app/db/models/__init__.py. A model that's never
imported anywhere never registers with Base.metadata, so a ForeignKey on another model pointing at its table
fails with NoReferencedTableError — but only at request time (when the mapper actually resolves the relationship),
not at import time or under mypy/ruff. This one is easy to miss without actually running the endpoint (step 9
below is exactly why that step isn't optional). Relatedly: don't import app.db.models inside app/main.py to
trigger this registration — that binds the name app in that module's namespace, colliding with the
app = FastAPI(...) variable. Use from app.db.models import Owner, Pet, ... instead.docs/use-cases/UC-*.md — main success scenario, every alternative flow, business
rules (BR-xxx), pre/postconditions. The response payloads and status codes for each alternative flow are not
optional detail — they're the spec.docs/guidelines/entity_model.md.HTTPException) that the router translates to responses.entity_model.md exactly and respecting the case-sensitivity
rule for this specific query."already exists" field error, UC-005 A1's 404).uv run uvicorn app.main:app --reload) and manually exercise the main flow and every
alternative flow. If any flow fails, or its status/payload doesn't match the use case exactly, fix it and
re-run. Repeat until every flow — main and every alternative — passes; don't consider the use case implemented
while any of them is broken.The thin-router / domain-exception pattern referenced throughout this skill, made concrete with UC-007 BR-001
(case-insensitive duplicate pet name → the "already exists" field error). Register exception handlers once in
main.py so individual routers never need a try/except — that's what keeps them thin.
# app/services/exceptions.py
class DomainError(Exception):
"""Base for business-rule violations the router layer translates to HTTP responses."""
class DuplicateNameError(DomainError):
def __init__(self, field: str, message: str = "already exists"):
self.field = field
self.message = message
class NotFoundError(DomainError):
pass
# app/services/pet_service.py
def add_pet(db: Session, owner_id: int, data: PetCreate) -> Pet:
exists = (
db.query(Pet)
.filter(Pet.owner_id == owner_id, func.lower(Pet.name) == data.name.lower())
.first()
)
if exists:
raise DuplicateNameError(field="name")
pet = Pet(owner_id=owner_id, **data.model_dump())
db.add(pet)
db.commit()
return pet
# app/main.py — registered once, applies to every router
@app.exception_handler(DuplicateNameError)
async def duplicate_name_handler(request: Request, exc: DuplicateNameError):
return JSONResponse(status_code=400, content={"field": exc.field, "error": exc.message})
@app.exception_handler(NotFoundError)
async def not_found_handler(request: Request, exc: NotFoundError):
return JSONResponse(status_code=404, content={"error": "not found"})
# app/routers/pets.py — thin: parse/validate, delegate, nothing else
@router.post("/owners/{owner_id}/pets", status_code=201)
def create_pet(owner_id: int, data: PetCreate, db: Session = Depends(get_db)):
pet = pet_service.add_pet(db, owner_id, data)
return PetOut.model_validate(pet)Follow this shape for the other domain exceptions this project needs (e.g. an ownership-mismatch error for UC-009
BR-003) — one small exception class per business rule, one handler each, routers stay free of try/except.
docs/guidelines/architecture.md §2.2–§2.4 — layering, auth boundary, pagination contract, case sensitivity,
connection management.docs/guidelines/entity_model.md — entities, columns, constraints.docs/use-cases/UC-*.md — the actual behavior to implement.aiup-core) for FastAPI/SQLAlchemy/Pydantic library documentation.../../rules/mcp-servers.md).dee4c03
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.