Skip to content

Commit 00fc3f5

Browse files
magicmarkclaude
andcommitted
Add extensions support to GraphQLRequest
Support the `extensions` field on GraphQL requests, as defined in the GraphQL over HTTP spec. This allows passing protocol extensions (e.g. persisted query IDs) as a top-level key in the request payload. Closes #590 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6d3ffad commit 00fc3f5

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

gql/graphql_request.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def __init__(
1313
*,
1414
variable_values: Optional[Dict[str, Any]] = None,
1515
operation_name: Optional[str] = None,
16+
extensions: Optional[Dict[str, Any]] = None,
1617
):
1718
"""Initialize a GraphQL request.
1819
@@ -21,6 +22,10 @@ def __init__(
2122
:param variable_values: Dictionary of input parameters (Default: None).
2223
:param operation_name: Name of the operation that shall be executed.
2324
Only required in multi-operation documents (Default: None).
25+
:param extensions: Dictionary of protocol extensions (Default: None).
26+
This is passed as the top-level ``extensions`` key in the request
27+
payload, as defined in the `GraphQL over HTTP spec
28+
<https://github.com/graphql/graphql-over-http>`_.
2429
:return: a :class:`GraphQLRequest <gql.GraphQLRequest>`
2530
which can be later executed or subscribed by a
2631
:class:`Client <gql.client.Client>`, by an
@@ -42,9 +47,12 @@ def __init__(
4247
variable_values = request.variable_values
4348
if operation_name is None:
4449
operation_name = request.operation_name
50+
if extensions is None:
51+
extensions = request.extensions
4552

4653
self.variable_values: Optional[Dict[str, Any]] = variable_values
4754
self.operation_name: Optional[str] = operation_name
55+
self.extensions: Optional[Dict[str, Any]] = extensions
4856

4957
def serialize_variable_values(self, schema: GraphQLSchema) -> "GraphQLRequest":
5058

@@ -61,6 +69,7 @@ def serialize_variable_values(self, schema: GraphQLSchema) -> "GraphQLRequest":
6169
operation_name=self.operation_name,
6270
),
6371
operation_name=self.operation_name,
72+
extensions=self.extensions,
6473
)
6574

6675
@property
@@ -74,6 +83,9 @@ def payload(self) -> Dict[str, Any]:
7483
if self.variable_values:
7584
payload["variables"] = self.variable_values
7685

86+
if self.extensions:
87+
payload["extensions"] = self.extensions
88+
7789
return payload
7890

7991
def __str__(self):

tests/test_graphql_request.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,57 @@ def test_graphql_request_init_with_graphql_request():
236236
assert request_1.variable_values["money"] == money_value_1
237237
assert request_2.variable_values["money"] == money_value_1
238238
assert request_3.variable_values["money"] == money_value_2
239+
240+
241+
def test_graphql_request_extensions_in_payload():
242+
extensions = {"persistedQuery": {"version": 1, "sha256Hash": "abc123"}}
243+
request = GraphQLRequest("{balance}", extensions=extensions)
244+
245+
payload = request.payload
246+
assert payload["extensions"] == extensions
247+
248+
249+
def test_graphql_request_extensions_not_in_payload_when_none():
250+
request = GraphQLRequest("{balance}")
251+
assert "extensions" not in request.payload
252+
253+
254+
def test_graphql_request_extensions_copied_from_graphql_request():
255+
extensions = {"persistedQuery": {"version": 1, "sha256Hash": "abc123"}}
256+
request_1 = GraphQLRequest("{balance}", extensions=extensions)
257+
request_2 = GraphQLRequest(request_1)
258+
259+
assert request_2.extensions == extensions
260+
assert request_2.payload["extensions"] == extensions
261+
262+
263+
def test_graphql_request_extensions_override_from_graphql_request():
264+
extensions_1 = {"persistedQuery": {"version": 1, "sha256Hash": "abc123"}}
265+
extensions_2 = {"custom": "value"}
266+
request_1 = GraphQLRequest("{balance}", extensions=extensions_1)
267+
request_2 = GraphQLRequest(request_1, extensions=extensions_2)
268+
269+
assert request_2.extensions == extensions_2
270+
271+
272+
def test_graphql_request_extensions_preserved_by_serialize_variable_values():
273+
extensions = {"persistedQuery": {"version": 1, "sha256Hash": "abc123"}}
274+
money_value = Money(10, "DM")
275+
276+
request = GraphQLRequest(
277+
"query myquery($money: Money) {toEuros(money: $money)}",
278+
variable_values={"money": money_value},
279+
extensions=extensions,
280+
)
281+
282+
serialized = request.serialize_variable_values(schema)
283+
assert serialized.extensions == extensions
284+
assert serialized.payload["extensions"] == extensions
285+
286+
287+
def test_graphql_request_str_includes_extensions():
288+
extensions = {"key": "value"}
289+
request = GraphQLRequest("{balance}", extensions=extensions)
290+
result = str(request)
291+
assert "extensions" in result
292+
assert "'key': 'value'" in result

0 commit comments

Comments
 (0)