Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ that explains how to use that feature.
progressive override labels.
- [`query_planner`](./query_planner): Add safety limits and debugging for query
planning.
- [`response_extensions`](./response_extensions): Propagate the GraphQL `extensions`
returned by subgraphs to the client response.
- [`supergraph`](./supergraph): Tell the router where to find your supergraph schema.
- [`traffic_shaping`](./traffic_shaping): Manage connection pooling and request
handling to subgraphs.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
title: "response_extensions"
---

import { Callout } from "@hive/design-system/hive-components/callout";

A GraphQL response can carry a top-level `extensions` object, a free-form JSON object used to attach
metadata (cache hints, tracing, warnings, timing, and other custom data) alongside `data` and `errors` keys.

By default, the router does **not** forward `extensions` returned by your subgraphs - they are
dropped. The `response_extensions` configuration lets you opt in and propagate them to the final
client response, with full control over which keys are forwarded and how values from multiple
subgraph responses are merged together.

## Configuration Structure

```yaml title="router.config.yaml"
response_extensions:
propagate:
algorithm: last # first | last | append. default: last
allow: # optional key whitelist. omit to forward all keys
- foo
- bar
```

Propagation is only active when `response_extensions.propagate` is present. Without it, behavior is
unchanged and nothing is forwarded.

| Key | Type | Description |
| :---------- | :--------- | :----------------------------------------------------------------------------------------------- |
| `algorithm` | `string` | How to merge an extension key seen across multiple subgraph responses. Default: `last`. |
| `allow` | `string[]` | Optional whitelist of top-level extension keys to forward. When omitted, all keys are forwarded. |

## Merge Algorithms

Because a single GraphQL operation can fan out to multiple subgraphs, the same extension key can
appear in several responses. The `algorithm` setting decides what the client sees and how merging is performed:

- `last` **(default)** - the last subgraph to respond wins. Good for scalar metadata where any value
is equally valid.
- `first` - the first subgraph to respond wins. Useful when you want a stable value and don't want
later subgraphs to overwrite it.
- `append` - every value is collected into an array, **always an array even when only one subgraph
contributed**. Use this when you want to keep all values (e.g. cache hints, tracing spans, or
warnings from multiple services).

### Example

Two subgraphs both return an `extensions.foo` key, with subgraph `a` responding before `b`:

```json
// subgraph a
{ "extensions": { "foo": { "some": ["array"] } } }

// subgraph b
{ "extensions": { "foo": { "some": "object" } } }
```

| `algorithm` | client sees |
| :---------- | :--------------------------------------------------------------------------- |
| `first` | `{ "extensions": { "foo": { "some": ["array"] } } }` |
| `last` | `{ "extensions": { "foo": { "some": "object" } } }` |
| `append` | `{ "extensions": { "foo": [{ "some": ["array"] }, { "some": "object" }] } }` |

With `append`, even a single contributing subgraph produces an array, so clients can consume it
without special-casing:

```json
{ "extensions": { "foo": [{ "some": ["array"] }] } }
```

## Key Whitelist

The optional `allow` list restricts propagation to specific top-level extension keys. When omitted,
all keys from all subgraphs are forwarded. Keys not in the list are silently dropped.

```yaml title="router.config.yaml"
response_extensions:
propagate:
algorithm: last
allow:
- cacheControl
- warnings
```

With the config above, only `cacheControl` and `warnings` reach the client; any other key a subgraph
sends is ignored.

## Precedence

Extension keys set by the router itself or by [plugins](/docs/router/customizations/plugins) always
take precedence over subgraph-propagated values. If a plugin sets `extensions.foo` and a subgraph
also returns `extensions.foo`, the plugin's value wins.

<Callout type="warning">
The `queryPlan` key is permanently reserved by the router and is never
propagated from subgraphs, regardless of your config - even if you add it to
the `allow` list.
</Callout>

## Ordering and Determinism

`first` and `last` are relative to subgraph **response order**, not plan order. For sequential plan
nodes the order is deterministic. For parallel fetches it depends on which subgraph responds first -
the same non-determinism that already applies to
[response header propagation](/docs/router/configuration/headers). If you need stable output under
parallel fetches, use `append` and sort on the client.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: Propagate Subgraph Extensions in Hive Router
description:
Hive Router can now forward the `extensions` returned by your subgraphs to the final client
response, with control over which keys propagate and how values from multiple subgraphs are merged.
date: 2026-06-30
authors: [denis]
---

A GraphQL response can carry a top-level `extensions` object - a free-form map where services attach
metadata like cache hints, tracing spans, or warnings alongside `data` and `errors`. Until now,
[Hive Router](/docs/router) dropped the `extensions` returned by your subgraphs.

You can now opt in and propagate them to the client, configured with the new top-level
`response_extensions` block:

```yaml title="router.config.yaml"
response_extensions:
propagate:
algorithm: last # first | last | append. default: last
allow: # optional key whitelist. omit to forward all keys
- cacheControl
- warnings
```

Without this config, behavior is unchanged and nothing is forwarded.

## Merging Across Subgraphs

Because a single operation can fan out to multiple subgraphs, the same extension key can show up in
several responses. The `algorithm` setting decides what the client sees:

- `last` **(default)** - the last subgraph to respond wins.
- `first` - the first subgraph to respond wins.
- `append` - every value is collected into an array, always an array even for a single value.

For example, if subgraph `a` and `b` both return `extensions.foo`, `append` gives the client every
contribution:

```json
{ "extensions": { "foo": [{ "some": ["array"] }, { "some": "object" }] } }
```

---

- [`response_extensions` configuration reference](/docs/router/configuration/response_extensions)
Loading