Files
openapi-first/docs/wiki/03_use_cases/02_templates.md

168 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Use Case 2: Templates — Copyable Reference Applications
`openapi-first` ships four runnable, copyable applications under `openapi_first/templates/`. They are **not part of the library API** — they are bundled scaffold examples you copy into your own project and build on.
---
## 🎬 1. What Templates Are
A template is a complete, self-contained OpenAPI-first service:
- A bundled directory inside `openapi_first/templates/<name>/`
- Copyable **verbatim** — no code generation, no mutation — via the CLI
- Each one demonstrates a specific set of OpenAPI-first behaviors and FastAPI features
All templates share the same skeleton:
```
<name>_app/
├── __init__.py # explains the template + how to scaffold it
├── openapi.yaml # the contract (source of truth)
├── main.py # assembles OpenAPIFirstApp from the spec
├── routes.py # operationId-bound handler functions
└── data.py # in-memory data store (demo only)
```
---
## 📋 2. The Four Templates
### 2.1 `health_app` — minimal liveness probe
```bash
openapi-first scaffold health_app
# or into a custom directory:
openapi-first scaffold health_app my-health-service
```
| File | Purpose |
|------|---------|
| `openapi.yaml` | `GET /health``operationId: get_health` |
| `routes.py` | `get_health()` returns `{"status": "ok"}` |
| `main.py` | `OpenAPIFirstApp(openapi_path="openapi.yaml", routes_module=routes)` |
**Why it exists:** the absolute minimal OpenAPI-first round trip — one operation, one handler, zero moving parts. The best starting point to internalize the mental model.
**Smoke test:**
```bash
pip install -e .
uvicorn main:app
curl http://localhost:8000/health
# → {"status": "ok"}
```
### 2.2 `crud_app` — dict-based CRUD
```bash
openapi-first scaffold crud_app my-crud-service
```
| File | Purpose |
|------|---------|
| `openapi.yaml` | Full CRUD over `/items` (list/get/create/update/delete) |
| `routes.py` | Handlers bound via `operationId`s `list_items`, `get_item`, `create_item`, `update_item`, `delete_item` |
| `data.py` | In-memory dict store with auto-incrementing `id` |
Behaviors you learn:
- **Explicit status codes** — `create_item`/`delete_item` take `response: Response` and set `201`/`204`; `get_item`/`update_item` raise `HTTPException(404)` on `KeyError`
- **Handlers as plain callables** — no FastAPI decorators, routing comes solely from the spec
- **Mock data store** — `data.py` is a copyable in-memory store, explicitly **not** production-ready
### 2.3 `model_app` — Pydantic model CRUD
```bash
openapi-first scaffold model_app my-model-service
```
| File | Purpose |
|------|---------|
| `openapi.yaml` | Same CRUD surface, schemas reference models |
| `models.py` | Pydantic `Item`, `ItemCreate`, `ItemBase` (request/response models) |
| `routes.py` | Handlers type-annotated with the models; `create_item` sets `201` |
| `data.py` | In-memory store returning real model instances |
Behaviors you learn:
- **Pydantic request/response models** — payloads validated and serialized via FastAPI
- **Same handler contracts** — identical `operationId` set as `crud_app`, so the two are interchangeable
- **Models in the client too** — the same spec drives `OpenAPIClient` body handling
### 2.4 `vet_app` — the full-featured demo
```bash
openapi-first scaffold vet_app my-vet-clinic
```
| File | Purpose |
|------|---------|
| `openapi.yaml` | Five resources (parents, vets, treatments, pets, appointments) + SSE + upload + discriminated unions |
| `models.py` | Pydantic models incl. **discriminated unions** (`noteType` literal fields) |
| `routes.py` | ~20 handlers across all resources, incl. pagination, filtering, photo upload, SSE streaming |
| `sse.py` | Server-Sent Events helper (`StreamingResponse`, per-pet subscriber queues, background `asyncio` workers) |
| `data.py` | Larger in-memory store (parents → vets → treatments → pets → appointments) |
| `main.py` | App + CORS + lifespan example |
Behaviors you learn — the *advanced* tier:
- **Discriminated unions** — `ProcedureNotes` uses `oneOf` + `discriminator.noteType` mapping; Pydantic models use `Literal[...]` discriminator fields
- **SSE streaming** — a `GET /pets/{id}/actions` operation streaming `text/event-stream` via `StreamingResponse` with background task workers
- **File upload** — `UploadFile` handler setting a multi-part body
- **CORS + middleware** — `add_middleware(CORSMiddleware, ...)` alongside the spec-driven setup
- **Response injection** — handlers set `201`/`204` explicitly via injected `Response`
---
## 🚀 3. CLI Reference
```bash
# List available templates
openapi-first scaffold --list
# Copy a template into its default directory (template name, dashes)
openapi-first scaffold health_app
# Copy into a custom target directory
openapi-first scaffold crud_app my-project/crud
```
> **Protip:** `DEFAULT_TEMPLATE` is `health_app`, so `openapi-first scaffold` with no template name scaffolds the health app.
---
## 🧩 4. Anatomy of a Scaffolded Service
After `openapi-first scaffold health_app my-health-service`, your directory contains a drop-in FastAPI service:
```
my-health-service/
├── openapi.yaml # THE contract
├── main.py # `app = OpenAPIFirstApp(openapi_path=..., routes_module=routes)`
└── routes.py # `def get_health(): ...`
```
Run it:
```bash
cd my-health-service
pip install -e .
uvicorn main:app --reload
```
`/docs`, `/openapi.json`, and every declared route now exist — all derived from `openapi.yaml`.
---
## ⚠️ 5. Production Disclaimer
Templates use **in-memory, non-persistent, non-concurrency-safe** data stores. They are learning scaffolds — **not** production references. Swap in a real data layer (SQL/REDIS/object store) the moment you go beyond a demo.
See the `__init__.py` of each template for detailed client examples, CLI examples, and design notes.
---
## Related
- [01 Quickstart](01_quickstart.md) · [04 Codegen](04_codegen.md) · [02 Components](../02_components.md)