Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4c3bb32
feat(limits): add per-subgraph response size limits
carodewig Apr 9, 2026
4ba780d
test: confirm function of into_bytes_limited
carodewig Apr 9, 2026
6c40d57
test: ensure configuration of limits works
carodewig Apr 9, 2026
3fec01f
test: ensure config and body limit work together
carodewig Apr 9, 2026
83554ee
doc: add subgraph response size limits to page
carodewig Apr 9, 2026
0421215
fix: only some errors are due to limit excess
carodewig Apr 9, 2026
86207e6
chore: add metric for when response is aborted
carodewig Apr 9, 2026
3daa5a5
doc: describe using metric to set limit
carodewig Apr 9, 2026
1f86a96
doc: add metric to standard instruments page
carodewig Apr 9, 2026
104b7ff
chore: record reason for stream stop in span
carodewig Apr 9, 2026
f7e32cf
doc: add span info to doc
carodewig Apr 9, 2026
18852b1
Revert "doc: add metric to standard instruments page"
carodewig Apr 9, 2026
d73f571
feat: implement for connectors as well
carodewig Apr 9, 2026
e704446
chore: regenerate schema snapshot for connector limits config
carodewig Apr 9, 2026
34537b4
chore: fmt and lint
carodewig Apr 9, 2026
2a876e7
chore: add changeset for subgraph/connector response size limits
carodewig Apr 9, 2026
b76a317
Merge branch 'dev' into caroline/subgraph-limits
carodewig Apr 9, 2026
474014e
test: config migration
carodewig Apr 9, 2026
935a710
docs: apply 8 AI review suggestions across 2 files
apollo-librarian[bot] Apr 9, 2026
c136467
Merge branch 'dev' into caroline/subgraph-limits
carodewig Apr 9, 2026
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
@@ -0,0 +1,35 @@
### Add per-subgraph and per-connector HTTP response size limits ([PR #9160](https://github.com/apollographql/router/pull/9160))

The router can now cap the number of bytes it reads from subgraph and connector HTTP response bodies, protecting against out-of-memory conditions when a downstream service returns an unexpectedly large payload.

The limit is enforced as the response body streams in — the router stops reading and returns a GraphQL error as soon as the limit is exceeded, without buffering the full body first.

Configure a global default and optional per-subgraph or per-source overrides:

```yaml
limits:
subgraph:
all:
http_max_response_bytes: 10485760 # 10 MB for all subgraphs
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use the friendly ByteSize to get something like 10mb?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I just cribbed from router.http_max_request_bytes but bytesize would be much friendlier.

subgraphs:
products:
http_max_response_bytes: 20971520 # 20 MB override for 'products'

connector:
all:
http_max_response_bytes: 5242880 # 5 MB for all connector sources
sources:
products.rest:
http_max_response_bytes: 10485760 # 10 MB override for 'products.rest'
```

There is no default limit; responses are unrestricted unless you configure this option.

When a response is aborted due to the limit, the router:
- Returns a GraphQL error to the client with extension code `SUBREQUEST_HTTP_ERROR`
- Increments the `apollo.router.limits.subgraph_response_size.exceeded` or `apollo.router.limits.connector_response_size.exceeded` counter
- Records `apollo.subgraph.response.aborted: "response_size_limit"` or `apollo.connector.response.aborted: "response_size_limit"` on the relevant span

**Configuration migration**: Existing `limits` fields (previously at the top level of `limits`) are now nested under `limits.router`. A configuration migration is included that updates your config file automatically.

By [@carodewig](https://github.com/carodewig) in https://github.com/apollographql/router/pull/9160
3 changes: 2 additions & 1 deletion apollo-router/benches/deeply_nested/router.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
supergraph:
listen: 127.0.0.1:44167
limits:
parser_max_recursion: ${env.PARSER_MAX_RECURSION}
router:
parser_max_recursion: ${env.PARSER_MAX_RECURSION}
include_subgraph_errors:
all: true
headers:
Expand Down
6 changes: 3 additions & 3 deletions apollo-router/src/axum_factory/listeners.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,9 @@ pub(super) fn serve_router_on_listen_addr(
configuration: Arc<Configuration>,
all_connections_stopped_sender: mpsc::Sender<()>,
) -> (impl Future<Output = Listener>, oneshot::Sender<()>) {
let opt_max_http1_headers = configuration.limits.http1_max_request_headers;
let opt_max_http1_buf_size = configuration.limits.http1_max_request_buf_size;
let opt_max_http2_headers_list_bytes = configuration.limits.http2_max_headers_list_bytes;
let opt_max_http1_headers = configuration.limits.router.http1_max_request_headers;
let opt_max_http1_buf_size = configuration.limits.router.http1_max_request_buf_size;
let opt_max_http2_headers_list_bytes = configuration.limits.router.http2_max_headers_list_bytes;
let connection_shutdown_timeout = configuration.supergraph.connection_shutdown_timeout;
let header_read_timeout = configuration.server.http.header_read_timeout;
let tls_handshake_timeout = configuration.server.http.tls_handshake_timeout;
Expand Down
2 changes: 1 addition & 1 deletion apollo-router/src/configuration/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ impl InstrumentData {

populate_config_instrument!(
apollo.router.config.limits,
"$.limits",
"$.limits.router",
opt.operation.max_depth,
"$[?(@.max_depth)]",
opt.operation.max_aliases,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
description: >
limits config restructured: existing fields moved under `limits.router`,
new `limits.subgraph` section added for per-subgraph response size limits.
actions:
- type: move
from: limits.http_max_request_bytes
to: limits.router.http_max_request_bytes
- type: move
from: limits.max_depth
to: limits.router.max_depth
- type: move
from: limits.max_height
to: limits.router.max_height
- type: move
from: limits.max_root_fields
to: limits.router.max_root_fields
- type: move
from: limits.max_aliases
to: limits.router.max_aliases
- type: move
from: limits.warn_only
to: limits.router.warn_only
- type: move
from: limits.parser_max_recursion
to: limits.router.parser_max_recursion
- type: move
from: limits.parser_max_tokens
to: limits.router.parser_max_tokens
- type: move
from: limits.http1_max_request_headers
to: limits.router.http1_max_request_headers
- type: move
from: limits.http1_max_request_buf_size
to: limits.router.http1_max_request_buf_size
- type: move
from: limits.http2_max_headers_list_bytes
to: limits.router.http2_max_headers_list_bytes
- type: move
from: limits.introspection_max_depth
to: limits.router.introspection_max_depth
Loading
Loading