Use when creating, extending, or restructuring FastAPI endpoints. Covers the full vertical slice: Pydantic request/response models, route handler, service method, repository method, dependency wiring, and pytest integration test stub. Trigger on: "add endpoint", "new route", "create API for", "add a GET/POST/PUT/DELETE", "build a resource", "add CRUD for".
This skill implements a complete vertical slice for a new FastAPI endpoint, following Clean Architecture principles. Every layer is created together so nothing is left unwired.
src/models/<resource>.pySee templates/all-templates.md (models section) for the canonical pattern.
Templates are intentionally consolidated in this single file; there are no
separate templates/models-template.py or sibling files.
<Resource>Base with shared fields<Resource>Create (input, no id)<Resource>Update (all fields optional, for PATCH)<Resource>Response (output, includes id, timestamps)model_config = ConfigDict(from_attributes=True) on response modelssrc/repositories/<resource>.py)See templates/all-templates.md (repository section) for the canonical pattern.
Abstract<Resource>Repository(ABC) with typed method signaturesSQLAlchemy<Resource>Repository that inherits the abstract classget_by_id, get_all, create, update, deleteasync; use AsyncSessionsrc/services/<resource>_service.py)See templates/all-templates.md (service section) for the canonical pattern.
<Resource>NotFoundError (a domain exception) when entity is missingsrc/api/routes/<resource>.py)See templates/all-templates.md (router section) for the canonical pattern.
APIRouter(prefix="/<resources>", tags=["<Resources>"])Depends() for service injectionsrc/api/main.py or src/main.py)from src.api.routes.users import router as users_router
app.include_router(users_router)
tests/integration/test_<resource>_routes.py)See templates/all-templates.md (test section) for the canonical pattern.
httpx.AsyncClient with ASGITransportBefore finishing, confirm these files exist and are wired:
src/models/<resource>.py — Pydantic modelssrc/repositories/<resource>.py — abstract + SQLAlchemy implementationsrc/services/<resource>_service.py — business logicsrc/api/routes/<resource>.py — FastAPI routersrc/api/main.py — router registeredtests/integration/test_<resource>_routes.py — integration testssrc/exceptions.py — domain exception addedAlways add a typed exception to src/exceptions.py:
class UserNotFoundError(Exception):
def __init__(self, user_id: uuid.UUID) -> None:
super().__init__(f"User {user_id} not found")
self.user_id = user_id
Register the exception handler in main.py:
@app.exception_handler(UserNotFoundError)
async def user_not_found_handler(request: Request, exc: UserNotFoundError):
return JSONResponse(status_code=404, content={"detail": str(exc)})