Skip to content

Commit e74f53d

Browse files
authored
docs(router): response_extensions (#138)
1 parent fb060fe commit e74f53d

3 files changed

Lines changed: 155 additions & 0 deletions

File tree

packages/documentation/content/docs/router/configuration/index.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ that explains how to use that feature.
3030
progressive override labels.
3131
- [`query_planner`](./query_planner): Add safety limits and debugging for query
3232
planning.
33+
- [`response_extensions`](./response_extensions): Propagate the GraphQL `extensions`
34+
returned by subgraphs to the client response.
3335
- [`supergraph`](./supergraph): Tell the router where to find your supergraph schema.
3436
- [`traffic_shaping`](./traffic_shaping): Manage connection pooling and request
3537
handling to subgraphs.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
title: "response_extensions"
3+
---
4+
5+
import { Callout } from "@hive/design-system/hive-components/callout";
6+
7+
A GraphQL response can carry a top-level `extensions` object, a free-form JSON object used to attach
8+
metadata (cache hints, tracing, warnings, timing, and other custom data) alongside `data` and `errors` keys.
9+
10+
By default, the router does **not** forward `extensions` returned by your subgraphs - they are
11+
dropped. The `response_extensions` configuration lets you opt in and propagate them to the final
12+
client response, with full control over which keys are forwarded and how values from multiple
13+
subgraph responses are merged together.
14+
15+
## Configuration Structure
16+
17+
```yaml title="router.config.yaml"
18+
response_extensions:
19+
propagate:
20+
algorithm: last # first | last | append. default: last
21+
allow: # optional key whitelist. omit to forward all keys
22+
- foo
23+
- bar
24+
```
25+
26+
Propagation is only active when `response_extensions.propagate` is present. Without it, behavior is
27+
unchanged and nothing is forwarded.
28+
29+
| Key | Type | Description |
30+
| :---------- | :--------- | :----------------------------------------------------------------------------------------------- |
31+
| `algorithm` | `string` | How to merge an extension key seen across multiple subgraph responses. Default: `last`. |
32+
| `allow` | `string[]` | Optional whitelist of top-level extension keys to forward. When omitted, all keys are forwarded. |
33+
34+
## Merge Algorithms
35+
36+
Because a single GraphQL operation can fan out to multiple subgraphs, the same extension key can
37+
appear in several responses. The `algorithm` setting decides what the client sees and how merging is performed:
38+
39+
- `last` **(default)** - the last subgraph to respond wins. Good for scalar metadata where any value
40+
is equally valid.
41+
- `first` - the first subgraph to respond wins. Useful when you want a stable value and don't want
42+
later subgraphs to overwrite it.
43+
- `append` - every value is collected into an array, **always an array even when only one subgraph
44+
contributed**. Use this when you want to keep all values (e.g. cache hints, tracing spans, or
45+
warnings from multiple services).
46+
47+
### Example
48+
49+
Two subgraphs both return an `extensions.foo` key, with subgraph `a` responding before `b`:
50+
51+
```json
52+
// subgraph a
53+
{ "extensions": { "foo": { "some": ["array"] } } }
54+
55+
// subgraph b
56+
{ "extensions": { "foo": { "some": "object" } } }
57+
```
58+
59+
| `algorithm` | client sees |
60+
| :---------- | :--------------------------------------------------------------------------- |
61+
| `first` | `{ "extensions": { "foo": { "some": ["array"] } } }` |
62+
| `last` | `{ "extensions": { "foo": { "some": "object" } } }` |
63+
| `append` | `{ "extensions": { "foo": [{ "some": ["array"] }, { "some": "object" }] } }` |
64+
65+
With `append`, even a single contributing subgraph produces an array, so clients can consume it
66+
without special-casing:
67+
68+
```json
69+
{ "extensions": { "foo": [{ "some": ["array"] }] } }
70+
```
71+
72+
## Key Whitelist
73+
74+
The optional `allow` list restricts propagation to specific top-level extension keys. When omitted,
75+
all keys from all subgraphs are forwarded. Keys not in the list are silently dropped.
76+
77+
```yaml title="router.config.yaml"
78+
response_extensions:
79+
propagate:
80+
algorithm: last
81+
allow:
82+
- cacheControl
83+
- warnings
84+
```
85+
86+
With the config above, only `cacheControl` and `warnings` reach the client; any other key a subgraph
87+
sends is ignored.
88+
89+
## Precedence
90+
91+
Extension keys set by the router itself or by [plugins](/docs/router/customizations/plugins) always
92+
take precedence over subgraph-propagated values. If a plugin sets `extensions.foo` and a subgraph
93+
also returns `extensions.foo`, the plugin's value wins.
94+
95+
<Callout type="warning">
96+
The `queryPlan` key is permanently reserved by the router and is never
97+
propagated from subgraphs, regardless of your config - even if you add it to
98+
the `allow` list.
99+
</Callout>
100+
101+
## Ordering and Determinism
102+
103+
`first` and `last` are relative to subgraph **response order**, not plan order. For sequential plan
104+
nodes the order is deterministic. For parallel fetches it depends on which subgraph responds first -
105+
the same non-determinism that already applies to
106+
[response header propagation](/docs/router/configuration/headers). If you need stable output under
107+
parallel fetches, use `append` and sort on the client.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
title: Propagate Subgraph Extensions in Hive Router
3+
description:
4+
Hive Router can now forward the `extensions` returned by your subgraphs to the final client
5+
response, with control over which keys propagate and how values from multiple subgraphs are merged.
6+
date: 2026-06-30
7+
authors: [denis]
8+
---
9+
10+
A GraphQL response can carry a top-level `extensions` object - a free-form map where services attach
11+
metadata like cache hints, tracing spans, or warnings alongside `data` and `errors`. Until now,
12+
[Hive Router](/docs/router) dropped the `extensions` returned by your subgraphs.
13+
14+
You can now opt in and propagate them to the client, configured with the new top-level
15+
`response_extensions` block:
16+
17+
```yaml title="router.config.yaml"
18+
response_extensions:
19+
propagate:
20+
algorithm: last # first | last | append. default: last
21+
allow: # optional key whitelist. omit to forward all keys
22+
- cacheControl
23+
- warnings
24+
```
25+
26+
Without this config, behavior is unchanged and nothing is forwarded.
27+
28+
## Merging Across Subgraphs
29+
30+
Because a single operation can fan out to multiple subgraphs, the same extension key can show up in
31+
several responses. The `algorithm` setting decides what the client sees:
32+
33+
- `last` **(default)** - the last subgraph to respond wins.
34+
- `first` - the first subgraph to respond wins.
35+
- `append` - every value is collected into an array, always an array even for a single value.
36+
37+
For example, if subgraph `a` and `b` both return `extensions.foo`, `append` gives the client every
38+
contribution:
39+
40+
```json
41+
{ "extensions": { "foo": [{ "some": ["array"] }, { "some": "object" }] } }
42+
```
43+
44+
---
45+
46+
- [`response_extensions` configuration reference](/docs/router/configuration/response_extensions)

0 commit comments

Comments
 (0)