# Spec-First Routing ## Summary The OpenAPI document is the single source of truth for the HTTP contract. Handlers are bound to it by name; there is no decorator router. --- ## The contract - `openapi.yaml` (or `.json`) declares paths, schemas, parameters, responses, and security. - A custom `operationId` names the handler function. - The framework (`openapi_first`) validates requests/responses against the spec and dispatches to the handler whose name equals `operationId`. ## App bootstrap ```python # main.py from openapi_first.app import OpenAPIFirstApp app = OpenAPIFirstApp( openapi_path="openapi.yaml", routes_module=interfaces.openapi, # module whose namespace holds handlers title=" API", version="0.1.0", lifespan=lifespan, ) ``` `lifespan` calls the composition root: ```python @asynccontextmanager async def lifespan(app): app.state.resources = await init() yield await shutdown() ``` ## Handler registry `interfaces/openapi.py` is a flat re-export of every handler, so the framework can resolve `operationId` across the whole app: ```python from interfaces.item.routes import * from interfaces.wallet.routes import * from interfaces.progress.sse import * ``` ## Handler shape Handlers are thin and synchronous with respect to business logic: ```python async def create_item(request: Request, body: dict): svc = request.app.state.resources["item_svc"] return await svc.create(Item.model_validate(body)) ``` ## Conventions - `operationId` naming: `list_`, `create_`, `get_`, `update_`, `delete_`. - Read-only endpoints may use a cache decorator keyed on path + query + user. - Validation errors -> `400`; not found -> `404`; delete -> `204`. - Security is declared once in the spec and enforced by the framework; handlers contain no auth code. ## Codegen The `openapi_first` library can scaffold models and route stubs from the spec, and ships app templates (CRUD app, model app, service). Prefer codegen over hand-writing boilerplate.