Use Case 3: The OperationId-Driven Client
OpenAPIClient is the other side of the contract. It reads the same OpenAPI document the server runs on and exposes one callable per operationId โ so "client" and "server" are two views of one truth.
๐ 1. Before You Start
The client is httpx-based and returns raw httpx.Response objects โ no magic deserialization, no hidden schema inference:
- No response Pydantic models
- No implicit URL construction (path params are explicit)
- No hand-written
requests.get(...)scattered through your code
๐งฌ 2. Constructing the Client
The base URL comes from the spec's servers list (first entry) unless you pass base_url explicitly:
You can also hand over a preconfigured httpx.Client (custom transport, TLS, retries):
๐ฅ 3. Fail-Fast at Construction
OpenAPIClient(...) raises immediately if the contract is broken โ you find out at startup, not on the first request:
| Violation | Error |
|---|---|
Spec has no servers entry |
OpenAPIClientError |
Spec has no paths |
OpenAPIClientError |
Operation missing operationId |
OpenAPIClientError |
Duplicate operationId |
OpenAPIClientError |
| Operation references unknown parameters | OpenAPIClientError |
Missing required parameters fail at call time โ pydoclint-grade strictness on the wire.
๐ 4. Calling Operations
Every operationId becomes a method. The call signature is uniform across the whole client:
Concrete examples (from the crud_app / health_app templates):
Returns: the raw httpx.Response, so status_code, .json(), .headers are all yours to inspect.
๐ง 5. How Parameters Are Bound
For each operation the client knows exactly where each parameter belongs:
| OpenAPI location | Client kwarg |
|---|---|
in: path |
path_params[name] |
in: query |
query[name] |
in: header |
headers[name] |
requestBody |
body |
JSON media types are sent as json=; other media types as raw content=.
๐ 6. Server โ Client, One Spec
Or the same client against a remote environment:
The URL is the only thing that changes between environments โ the contract never does.
โ๏ธ 7. OperationId as the API
Because the client is operationId-driven:
- Adding an operation = adding a method (and vice versa)
- No operation can be "forgotten" by the client โ it's constructed from the spec
operationIdis the one name you remember; the HTTP verb/path is an implementation detail
If an operationId you call doesn't exist, you get an AttributeError at construction-scan time โ never a silent 404.