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?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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.