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:
2026-09-15 22:29:05 +05:30
parent adeb02e162
commit b559323dfe
26 changed files with 1590 additions and 86 deletions

157
docs/wiki/01_overview.md Normal file
View 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)