docs: add OpenAPI-first wiki (overview, components, use cases for templates/client/codegen, design, security, error handling, testing) and refresh lib docs index
This commit is contained in:
157
docs/wiki/01_overview.md
Normal file
157
docs/wiki/01_overview.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Overview — The OpenAPI-First Mental Model
|
||||
|
||||
`openapi-first` inverts the usual FastAPI workflow. Instead of decorating routes in code and letting FastAPI *invent* an OpenAPI document for you, you write **one OpenAPI document first** and let the library assemble both the application **and** the client from it. The spec is the contract; code is the implementation.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 1. The Mental Model
|
||||
|
||||
### 1.1 One source of truth
|
||||
|
||||
Your OpenAPI document (`openapi.yaml` or `openapi.json`) is the **single authoritative contract**:
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
/health:
|
||||
get:
|
||||
operationId: get_health
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
```
|
||||
|
||||
Every route, method, parameter, schema, and security requirement lives **here** — and only here. Code never declares routes.
|
||||
|
||||
### 1.2 `operationId` is the binding key
|
||||
|
||||
The only bridge between the spec and your Python code is the `operationId`. Each operation maps, **by name**, to exactly one plain callable:
|
||||
|
||||
```python
|
||||
# routes.py
|
||||
def get_health():
|
||||
"""Health check operation handler."""
|
||||
return {"status": "ok"}
|
||||
```
|
||||
|
||||
`openapi_first` resolves `operationId: get_health` → `routes.get_health` and registers the route. **No decorators, no `@app.get`, no routing metadata in code.**
|
||||
|
||||
Guarantees this binding provides:
|
||||
|
||||
- 🚫 No undocumented route can exist — every route *must* be in the spec
|
||||
- 🚫 No spec operation can go unhandled — every `operationId` *must* resolve at startup
|
||||
- 🔒 No auth can be bypassed — security is injected from `securitySchemes` + per-operation `security`, spec-driven
|
||||
- 🧱 No drift possible — the server and client are built from the *same* document
|
||||
|
||||
### 1.3 Fail-fast by design
|
||||
|
||||
Contract violations are detected **at application startup** (or client construction), never silently at request time:
|
||||
|
||||
| What goes wrong | When it fails |
|
||||
|------------------------------------------|----------------------|
|
||||
| Invalid / unloadable spec | `load_openapi` at startup |
|
||||
| Spec fails OpenAPI 3.x validation | `load_openapi` at startup |
|
||||
| `operationId` missing a handler | `bind_routes` at startup |
|
||||
| Operation declared but no `operationId` | `bind_routes` at startup |
|
||||
| Missing / duplicate `operationId` in client | `OpenAPIClient(...)` construction |
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 2. What It Looks Like
|
||||
|
||||
### 2.1 Scaffold an application
|
||||
|
||||
```bash
|
||||
openapi-first scaffold health_app my-health-service
|
||||
openapi-first scaffold --list
|
||||
```
|
||||
|
||||
`scaffold` copies a bundled template (verbatim — no code generation, no mutation) into a directory of your choice.
|
||||
|
||||
### 2.2 Bootstrap the server
|
||||
|
||||
```python
|
||||
# main.py
|
||||
from openapi_first.app import OpenAPIFirstApp
|
||||
import routes
|
||||
|
||||
app = OpenAPIFirstApp(
|
||||
openapi_path="openapi.yaml",
|
||||
routes_module=routes,
|
||||
title="My Service",
|
||||
)
|
||||
```
|
||||
|
||||
Run with your FastAPI-compatible server (ASGI):
|
||||
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
FastAPI itself drives the server; every route, response model, and security dependency comes from the spec. `/openapi.json` and Swagger UI always reflect the spec, byte-for-byte.
|
||||
|
||||
### 2.3 Talk to it with the client
|
||||
|
||||
```python
|
||||
# client-side
|
||||
from openapi_first.loader import load_openapi
|
||||
from openapi_first.client import OpenAPIClient
|
||||
|
||||
spec = load_openapi("openapi.yaml")
|
||||
client = OpenAPIClient(spec)
|
||||
|
||||
response = client.get_health() # operationId-driven call
|
||||
print(response.status_code) # 200
|
||||
print(response.json()) # {"status": "ok"}
|
||||
```
|
||||
|
||||
`OpenAPIClient` builds one callable per `operationId` from the *same* spec, so the client can never drift from the server.
|
||||
|
||||
---
|
||||
|
||||
## 🧩 3. Where Things Live
|
||||
|
||||
| Concern | Module |
|
||||
|------------------------|---------------------|
|
||||
| Load + validate spec | `openapi_first.loader` |
|
||||
| Boot FastAPI app | `openapi_first.app` |
|
||||
| Bind routes by opId | `openapi_first.binder` |
|
||||
| HTTP client | `openapi_first.client` |
|
||||
| Error hierarchy | `openapi_first.errors` |
|
||||
| Security dependencies | `openapi_first.security` |
|
||||
| Pydantic model codegen | `openapi_first.codegen` (uses `datamodel_code_generator`) |
|
||||
| Route stub codegen | `openapi_first.codegen_routes` |
|
||||
| Models / routes CLI | `openapi_first.cli` |
|
||||
| Bundled templates | `openapi_first.templates` |
|
||||
|
||||
Jurisdictions:
|
||||
|
||||
- `loader`, `app`, `binder`, `client`, `errors`, `security` are the **library API surface** — stable, tested, documented.
|
||||
- `templates` are **copyable scaffolds** — not part of the library API; excluded from lint/format/type gates, never imported at runtime.
|
||||
|
||||
---
|
||||
|
||||
## 📄 4. The Non-Goals
|
||||
|
||||
`openapi-first` deliberately does **not**:
|
||||
|
||||
- Parse decorators to generate an OpenAPI schema (that's default FastAPI behavior — the inverse)
|
||||
- Generate models from code at runtime (only at **build time** via CLI, from spec → Pydantic)
|
||||
- Validate request/response bodies against the spec at runtime (contract is enforced at startup / client construction; FastAPI + Pydantic handle runtime coercion)
|
||||
- Invent routing from path conventions — `operationId` binding only
|
||||
- Ship a production feature set in the bundled templates (they're demos/scaffolds: in-memory stores, no concurrency, no auth configured)
|
||||
|
||||
---
|
||||
|
||||
## 🧭 5. Path Forward
|
||||
|
||||
New here? Start with [01 – Quickstart](03_use_cases/01_quickstart.md). Want to copy a runnable app? Jump to [02 – Templates](03_use_cases/02_templates.md). Digging into internals? See [Design](04_design.md).
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [01 – Quickstart](03_use_cases/01_quickstart.md) · [02 – Components](02_components.md) · [01 – Overview](01_overview.md)
|
||||
Reference in New Issue
Block a user