Python Client¶
A fully typed GraphQL client generated from your schema. Types, inputs and enums are
pydantic v2 models, every operation is an async method, and the
transport is a single injectable async function, so you can use httpx (generated for you), aiohttp,
or a test double.
Requires Python 3.10+ and pydantic >= 2.10. The default adapters need httpx >= 0.27 and
websockets >= 14; they are only imported by the generated adapter files, so with
"httpAdapter": "none" and "wsAdapter": "none" pydantic is the only dependency.
Configuration¶
Set "mode": "client" and add a "python" key under clientConfig:
The output directory is a Python package (__init__.py re-exports everything). All options are listed in the
configuration reference.
Using the client¶
import asyncio
from myapp.graphql import GraphLinkClient
async def main() -> None:
client = GraphLinkClient.with_http(
url="https://api.example.com/graphql",
ws_url="wss://api.example.com/graphql",
headers_provider=lambda: {"Authorization": "Bearer token"}, # sync or async
)
res = await client.queries.get_user(id="user-1")
print(res.get_user.name)
asyncio.run(main())
Operation methods take keyword-only arguments named like the schema arguments in snake_case
(client.mutations.update_user(id="1", input=UpdateUserInput(name="x"))). The result object is named after
the operation and exposes each root field as an attribute (res.get_user).
Bring your own adapter¶
The client only needs a coroutine that takes the JSON payload and returns the response body:
async def adapter(payload: str) -> str:
async with aiohttp.ClientSession() as session:
async with session.post(URL, data=payload, headers={"Content-Type": "application/json"}) as r:
return await r.text()
client = GraphLinkClient(adapter=adapter, ws_adapter=my_ws_adapter)
With operationNameAsParameter: true the adapter also receives the operation name as a second argument.
ws_adapter is required when the schema has subscriptions (subclass GraphLinkWebSocketAdapter, or use the
generated DefaultGraphLinkWebSocketAdapter).
Models¶
- Fields are
snake_case; the GraphQL name is kept as the pydantic alias, somodel_dump(by_alias=True)/to_json()produce the wire format andModel(first_name=...)andModel.from_json({"firstName": ...})both work. - Models are frozen unless
immutableTypeFieldsisfalse. - Nullable fields default to
None, unlessnullableFieldsRequiredistrue. Schema defaults become pydantic defaults. - Interfaces and unions are base models: validating a dict that carries
__typenamereturns the concrete class, and an unknown__typenameraisesValueError. - Python keywords and pydantic attribute names are escaped with a trailing underscore (
class→class_).
Enums¶
Enums are str-backed Enums with SCREAMING_SNAKE members that keep the wire value. Unless strictEnums is
true, a value the schema does not declare decodes to unknownValue__ instead of raising, and
encoding that member raises ValueError.
Subscriptions¶
Subscriptions are async generators:
async for event in client.subscriptions.user_status_changed(user_id="user-1"):
print(event.user_status_changed.status)
Breaking out of the loop (or cancelling the task) completes the subscription. All subscriptions share one WebSocket connection; the default adapter reconnects with exponential backoff and re-subscribes.
Errors¶
Operations raise GraphLinkException (its .errors is the list of GraphLinkError) when the response contains GraphQL
errors. Mark an operation with @glCaptureErrors (or set captureErrors: true) to get a …FullResponse with data and
errors instead.
Caching¶
@glCache and @glCacheInvalidate behave as in the other clients (see Caching). The default store is
in-memory; implement GraphLinkCacheStore (four async methods) and pass store= to persist it.
File uploads¶
Upload arguments take GLUpload objects; the multipart request uses the GraphQL multipart request spec:
from myapp.graphql import GLUpload
await client.mutations.upload_avatar(user_id="1", file=GLUpload.from_file("avatar.png"))
progress is reported through on_progress(sent, total); the default httpx adapter reports the start and the end of
the request.
Persisted queries¶
persistedQueries: "apq" (and optionally persistedQueriesUseGet) work as described in
Persisted Queries. With GET enabled, GraphLinkClient.with_http wires the GET adapter;
when building the client yourself pass get_adapter=.