Skip to content

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:

1
2
3
4
5
6
<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

1
2
3
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:

1
2
3
4
pip install -e .
uvicorn main:app
curl http://localhost:8000/health
# โ†’ {"status": "ok"}

2.2 crud_app โ€” dict-based CRUD

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 operationIds 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

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
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

1
2
3
4
5
6
7
8
# 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:

1
2
3
4
my-health-service/
โ”œโ”€โ”€ openapi.yaml   # THE contract
โ”œโ”€โ”€ main.py        # `app = OpenAPIFirstApp(openapi_path=..., routes_module=routes)`
โ””โ”€โ”€ routes.py      # `def get_health(): ...`

Run it:

1
2
3
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.