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
operationIdnames the handler function. - The framework (
openapi_first) validates requests/responses against the spec and dispatches to the handler whose name equalsoperationId.
App bootstrap
lifespan calls the composition root:
Handler registry
interfaces/openapi.py is a flat re-export of every handler, so the framework
can resolve operationId across the whole app:
Handler shape
Handlers are thin and synchronous with respect to business logic:
Conventions
operationIdnaming:list_<resource>,create_<resource>,get_<resource>,update_<resource>,delete_<resource>.- 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.
Authless apps
Auth is deferred in the standard: the same spec-first app runs with or without it, because security lives entirely in the spec, never in handlers. For an app that needs no auth yet, do not invent per-handler auth checks — just omit the security keys:
- Remove the top-level
security:block. - Remove
components.securitySchemes. - Do not add
security: []to operations. An explicit empty array is a per-operation override that means "public", and it only matters when a globalsecurityblock exists. Leaving every operation without asecuritykey is the cleanest authless spec.
The handlers, services, and composition root are unchanged. When auth is needed
later, add the global security block and an introspecting dependency; only
handlers that must opt out get security: [].
Codegen
openapi_first scaffolds models and route stubs from the spec (see the
Build Recipe, step 3):
Prefer codegen over hand-writing boilerplate. The result is the wire contract
and a set of operationId-named signatures — not the canonical handlers.