78 lines
2.0 KiB
Markdown
78 lines
2.0 KiB
Markdown
# 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="<App> 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_<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.
|
|
|
|
## 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.
|