Quick start¶
This page walks you from "I have an OpenAPI document" to "I have a typed
Python client I can import and call." Five minutes, four commands.
For a tour of every API surface the generated client exposes — filters, sorts, pagination, actions, async, error handling, retries — see Using the client.
1. Pick (or grab) a spec¶
okapipy reads OpenAPI 3.x in JSON or YAML, from a local file or an
http(s) URL. For the rest of this page we'll use a local
openapi.yaml, but anywhere you see that, a URL works too.
2. Sanity-check the parse¶
Before generating code, see what okapipy thinks your spec looks like:
You'll get:
- a counts panel on stderr (namespaces, collections, resources, singletons, actions),
- a JSON dump of the parsed tree on stdout — pipe it into
lessorjq.
If you'd rather save the tree:
okapipy spec parse openapi.yaml --output tree.yaml
# .yaml or .yml → YAML; .json → JSON. Anything else errors.
If a classification looks off (Staff should be a collection, me
should be a singleton, a non-CRUD POST got dropped), fix it in the rules
file — see Rules and extensions — before generating.
3. Generate a client¶
okapipy spec generate openapi.yaml \
--output ./my-client \
--package acme.commerce \
--client-class CommerceClient
That writes a complete Python project under ./my-client:
my-client/
├── pyproject.toml # one-shot
├── README.md # one-shot
├── LICENSE # one-shot
├── src/acme/commerce/
│ ├── __init__.py # one-shot — your public surface
│ ├── client.py # one-shot — class CommerceClient(CommerceClientBase)
│ ├── orders.py # one-shot — class Order(OrderBase), OrdersCollection(...)
│ └── base/ # regenerated every run — DO NOT EDIT
│ ├── client.py
│ ├── collections/orders.py
│ ├── resources/order.py
│ ├── actions/order_submit.py
│ ├── models.py
│ ├── strategies.py
│ ├── filters.py
│ ├── sort.py
│ ├── transport.py
│ ├── exceptions.py
│ ├── types.py
│ └── _manifest.json
└── tests/ # one-shot scaffolding
Two layers, two lifecycles: the base/ tree is rewritten on every
okapipy spec generate, the rest is emitted once and then yours
forever. See Code customization for the full story.
4. Use it¶
from acme.commerce import CommerceClient
with CommerceClient(base_url="https://api.example.com") as client:
# Iterate a collection — pagination is automatic.
for order in client.commerce.orders:
print(order.id, order.total)
# Look up one resource by id (square brackets, not parens).
order = client.commerce.orders["ord_42"].retrieve()
# Sub-collections walk the tree.
line = client.commerce.orders["ord_42"].lines.create(
body={"sku": "SKU-1", "qty": 2},
)
# Actions are real properties returning an Action object whose
# one method is `run(...)`.
client.commerce.orders["ord_42"].submit.run()
That's the whole rhythm. Three observations to internalize:
- Listing is iteration, not a
fetch()method.for order in client.commerce.orders:issues paged GETs through the configured pagination strategy and yields parsed model instances. - Resources use
[id], not(id). The collection implements__getitem__, returning the resource object — no network call yet. - Actions have a single
.run()method. POST/.../submitis exposed assubmit.run(), notsubmit(), because the action object itself can carry per-call state (overrides, headers, options).
The generated client is also built to be navigated by hover: every
class lists the children you can reach from it (sub-namespaces,
collections, actions, ...) right in its docstring, so typing client.
and reading the IDE tooltip is enough to discover the surface. See
IDE tooltips for the full story.
5. Iterate¶
When the upstream spec changes, re-run the same command:
okapipy spec generate openapi.yaml \
--output ./my-client \
--package acme.commerce \
--client-class CommerceClient
The base/ tree is rewritten to match the new spec. Your subclasses,
your client.py, your __init__.py — all left strictly alone. If the
spec gained a new namespace or collection, the generator emits new
one-shot stubs for it and prints a drift-detection warning telling
you which factory line to add to wire your subclass into the tree.
In CI, gate on regeneration being committed:
okapipy spec generate openapi.yaml --output ./my-client \
--package acme.commerce --client-class CommerceClient --check
--check exits non-zero if any base file would change, any drift
warning fires, or any stale base file would be pruned.
Useful flags¶
| Flag | What it does |
|---|---|
--rules path/to/rules.yaml |
Project-local override layer (see Rules and extensions). |
--strip-prefix /api/v1 |
Drop a base prefix from every path before classification. |
--lang en |
ISO language code for NLP (defaults to en). |
--nlp-cache-dir DIR |
Where to look for / store the spaCy model. |
--shape models\|dicts |
Lock the generated client to one response shape. Omit for the dual-shape default (constructor shape= + with_shape()). |
--templates-dir DIR |
Override packaged Jinja templates per project (see Templates). |
--model-templates-dir DIR |
Override datamodel-code-generator's model templates. |
--check |
CI dry-run: report drift, exit non-zero on any change. |
--quiet / -q |
Suppress drift-detection warnings (pruning still runs). |
-v / -vv |
INFO / DEBUG logging. |