168 lines
6.0 KiB
Markdown
168 lines
6.0 KiB
Markdown
# 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)
|