Architecture and design¶
stac-transaction-api-client is a small generated HTTP client adapted to use
PySTAC objects at the STAC Item boundary. Its runtime design separates transport,
endpoint behavior, STAC objects, and error models.
Source and generation pipeline¶
The repository keeps both the upstream and bundled OpenAPI descriptions under
schemas/. Taskfile.yaml defines two relevant tasks:
bundle_apidownloads the Transaction Extensionopenapi.yamlfromstac-api-extensions/transactionand bundles it locally.client_generationruns the shared OpenAPI client generator and then removes generated STAC model modules as part of the adaptation toward PySTAC.
The resulting endpoint modules live under:
src/stac_transaction_api_client/api/transaction/
The current endpoint set is get_feature, post_feature, update_feature,
patch_feature, and delete_feature.
Runtime layers¶
The client can be understood as four layers:
application code
|
v
transaction endpoint modules
|
+--> PySTAC Item serialization/deserialization
|
v
Client / AuthenticatedClient
|
v
httpx.Client / httpx.AsyncClient
|
v
STAC API
Transport clients¶
Client owns base URL, headers, cookies, timeout, TLS verification, redirect
behavior, and optional extra httpx arguments. It lazily creates either a sync
httpx.Client or async httpx.AsyncClient.
AuthenticatedClient adds token-based authentication. By default it sends:
Authorization: Bearer <token>
Both classes can also wrap an externally created httpx client, which is useful
when an application already manages connection pools, middleware, proxies, or
custom transports.
Endpoint modules¶
Each transaction operation is represented by a module rather than a method on the client object. A typical generated module performs four steps:
- build request arguments;
- send the request through the configured client;
- parse the HTTP response into a Python object;
- optionally wrap the result in
Response[T].
Mutation modules provide convenience functions that return only the parsed body
and *_detailed functions that preserve the complete response metadata.
get_feature currently exposes only detailed sync and async variants.
Why PySTAC is used¶
STAC Items already have a well-established Python representation in PySTAC.
Rather than maintaining a second generated model for the same domain object, the
endpoint modules serialize request bodies with Item.to_dict() and deserialize
successful item responses with Item.from_dict().
This means callers can construct, inspect, mutate, and validate items using the same PySTAC objects they use elsewhere in a STAC workflow.
PySTAC is declared as the pystac optional dependency in pyproject.toml, but
the transaction endpoint modules import it directly. Consequently,
stac-transaction-api-client[pystac] is the practical installation target for
transaction use.
Detailed versus parsed responses¶
The shared Response[T] type preserves:
- the
HTTPStatusvalue; - raw body bytes;
- response headers;
- the parsed result.
That distinction matters for the Transaction Extension because important protocol state can live outside the JSON body. ETags are the most obvious example: callers need response headers to implement optimistic locking.
Use a convenience sync(...) or asyncio(...) function when the parsed body is
all you need. Use sync_detailed(...) or asyncio_detailed(...) when status,
headers, or raw bytes influence application behavior.
Optimistic locking and ETags¶
The Transaction Extension supports optimistic locking through ETags. A client
first reads a resource and receives an ETag representing its current state. The
ETag is then sent in If-Match for a mutation. If the resource changed in the
meantime, the server can reject the stale write rather than silently overwrite a
newer version.
The current Python signatures encode that protocol choice explicitly:
update_featurerequiresif_match;delete_featurerequiresif_match;patch_featuremakesif_matchoptional.
This is why detailed GET responses are central to update and delete workflows:
the parsed pystac.Item contains the domain object, while the response headers
contain the concurrency token.
Where the generated typing does not fully match the specification¶
Reusing PySTAC simplifies complete-item payloads, while mappings represent partial PATCH payloads.
POST and ItemCollection¶
The Transaction Extension describes POST bodies as an Item or ItemCollection.
The post_feature module therefore accepts pystac.Item and
pystac.ItemCollection; it does not confuse bulk item creation with a STAC
Collection metadata object.
PATCH and JSON Merge Patch¶
The Transaction Extension defines PATCH according to JSON Merge Patch semantics.
The patch_feature body accepts a mapping containing only the fields to be
merged. It also retains pystac.Item support for applications that intentionally
send an item-shaped payload.
The underlying httpx client remains available for requests outside the
generated endpoint signatures.
Sync and async symmetry¶
The transport layer supports both blocking and asynchronous httpx clients.
Mutation modules mirror that with sync/sync_detailed and
asyncio/asyncio_detailed. This keeps endpoint semantics the same while
allowing the integration style to match the surrounding application.
Async clients can be used as context managers so connection pools are closed cleanly:
async with Client(base_url="https://stac.example.com") as client:
...