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

View File

@@ -0,0 +1,121 @@
# Error Handling — Fail Loud, Fail Early
`openapi-first` treats errors as **first-class contract documents**: every failure mode is a named exception with a stable import pathhare, and every one surfaces as early as possible.
---
## 🧬 1. The Hierarchy
All errors derive from `OpenAPIFirstError` (in `openapi_first/errors.py`), so a single `except OpenAPIFirstError` catches every first-party failure:
```text
OpenAPIFirstError
├── OpenAPISpecError # spec-level problems
│ └── OpenAPISpecLoadError # load / parse / validation (loader)
├── OpenAPIClientError # client-side contract issues (client)
└── MissingOperationHandler # spec op with no handler (binder)
# Security / loader layers raise through OpenAPISpecError subclasses too
```
| Exception | Module | Raised when |
|----------------------------------|-----------|-------------|
| `OpenAPISpecLoadError` | `loader` | Path missing, file unreadable, YAML/JSON invalid, or spec fails OpenAPI 3.x validation |
| `OpenAPIClientError` | `client` | No `servers`, no `paths`, missing/duplicate `operationId`, missing required params at construction |
| `MissingOperationHandler` | `errors` | An operation is declared whose `operationId` has no matching handler in `routes_module` |
---
## ⏱️ 2. When Things Fail
The single most important rule: **violations are eager, not lazy.**
### 2.1 At application startup
```python
# openapi.yaml missing ────────────────────────────► OpenAPISpecLoadError
# operationId without a handler ───────────────────► MissingOperationHandler
# operation with no operationId declared ──────────► MissingOperationHandler
```
Because these raise during `OpenAPIFirstApp(...)` construction, CI catches them the moment a spec and its routes drift — before a single request is served.
### 2.2 At client construction
```python
OpenAPIClient(spec) # fails fast, same philosophy
```
- Spec with no `servers` → `OpenAPIClientError`
- Spec with no `paths` → `OpenAPIClientError`
- Duplicate `operationId`s → `OpenAPIClientError` (client methods must be unambiguous)
- Operation missing `operationId` → `OpenAPIClientError`
### 2.3 At call time (client)
Runtime transport errors surface as `httpx` exceptions (`httpx.RequestError` family), not swallowed or remapped. Missing required args fail before any HTTP request is made:
```python
client.get_user(path_params={"user_id": ...}) # OK
client.get_user() # ValueError — user_id required
```
---
## 🧰 3. Handling in Your App
### 3.1 Server-side
Handlers raise FastAPI `HTTPException` for expected operation-level failures (404/422), and the `OperationId`-binding errors only exist at startup:
```python
from fastapi import HTTPException
def get_item(item_id: int):
"""Retrieve an item by ID.
Implements the OpenAPI operation ``get_item``.
Args:
item_id (int): Identifier of the item.
Raises:
HTTPException: If the item does not exist (404).
"""
try:
return _get_item(item_id)
except KeyError:
raise HTTPException(status_code=404, detail="Item not found")
```
### 3.2 Client-side
```python
import httpx
try:
response = client.get_item(path_params={"item_id": 1})
except httpx.HTTPStatusError as exc:
... # 4xx/5xx from the server
```
`httpx.HTTPStatusError` isn't raised by the library — it's the standard `httpx.raise_for_status()` you can opt into per call. The library never masks a response code.
---
## 🛡️ 4. Fail-Fast Guarantees Recap
| Layer | You write | The library guarantees |
|-------|-----------|------------------------|
| Loader | a spec path | unreadable/invalid specs never reach your app |
| Binder | handler functions | every operation must resolve, or the app won't start |
| Client | a spec | every operationId becomes a callable; missing ones fail at construction |
| Runtime | handler code | FastAPI + Pydantic handle coercion; contract checks already happened at startup |
None of these can silently degrade: a violation is an **exception at construction**, not a 500 at request time.
---
## Related
- [01 – Overview](01_overview.md) · [05 – Security](05_security.md) · [07 – Testing](07_testing.md)