Skip to content

Releases: apollographql/router

v2.11.0

27 Jan 15:04
01d3752

Choose a tag to compare

🚀 Features

Support client awareness metadata via HTTP headers (PR #8503)

Clients can now send library name and version metadata for client awareness and enhanced client awareness using HTTP headers. This provides a consistent transport mechanism instead of splitting values between headers and request.extensions.

By @calvincestari in #8503

Reload OCI artifacts when a tag reference changes (PR #8805)

You can now configure tag-based OCI references in the router. When you use a tag reference such as artifacts.apollographql.com/my-org/my-graph:prod, the router polls and reloads when that tag points to a new artifact.

This also applies to automatically generated variant tags and custom tags.

By @graytonio in #8805

Add memory limit option for cooperative cancellation (PR #8808)

The router now supports a memory_limit option on experimental_cooperative_cancellation to cap memory allocations during query planning. When the memory limit is exceeded, the router:

  • In enforce mode, cancels query planning and returns an error to the client.
  • In measure mode, records the cancellation outcome in metrics and allows query planning to complete.

The memory limit works alongside the existing timeout option. Whichever limit is reached first triggers cancellation.

This feature is only available on Unix platforms when the global-allocator feature is enabled and dhat-heap is not enabled.

Example configuration:

supergraph:
  query_planning:
    experimental_cooperative_cancellation:
      enabled: true
      mode: enforce  # or "measure" to only record metrics
      memory_limit: 50mb  # Supports formats like "50mb", "1gb", "1024kb", etc.
      timeout: 5s  # Optional: can be combined with memory_limit

By @rohan-b99 in #8808

Add memory tracking metrics for requests (PR #8717)

The router now emits two histogram metrics to track memory allocation activity during request processing:

  • apollo.router.request.memory: Memory activity across the full request lifecycle (including parsing, validation, query planning, and plugins)
  • apollo.router.query_planner.memory: Memory activity for query planning work in the compute job thread pool

Each metric includes:

  • allocation.type: allocated, deallocated, zeroed, or reallocated
  • context: The tracking context name (for example, router.request or query_planning)

This feature is only available on Unix platforms when the global-allocator feature is enabled and dhat-heap is not enabled.

By @rohan-b99 in #8717

🐛 Fixes

Support nullable @key fields in response caching (PR #8767)

Response caching can now use nullable @key fields. Previously, the response caching feature rejected nullable @key fields, which prevented caching in schemas that use them.

When you cache data keyed by nullable fields, keep your cache keys simple and avoid ambiguous null values.

By @aaronArinder in #8767

Return 429 instead of 503 when enforcing a rate limit (PR #8765)

In v2.0.0, the router changed the rate-limiting error from 429 (TOO_MANY_REQUESTS) to 503 (SERVICE_UNAVAILABLE). This change restores 429 to align with the router error documentation.

By @carodewig in #8765

Add status code and error type attributes to http_request spans (PR #8775)

The router now always adds the http.response.status_code attribute to http_request spans (for example, for router -> subgraph requests). The router also conditionally adds error.type for non-success status codes.

By @rohan-b99 in #8775

Report response cache invalidation failures as errors (PR #8813)

The router now returns an error when response cache invalidation fails. Previously, an invalidation attempt could fail without being surfaced as an error.

After you upgrade, you might see an increase in the apollo.router.operations.response_cache.invalidation.error metric.

By @bnjjj in #8813

Reuse response cache Redis connections for identical subgraph configuration (PR #8764)

The response cache now reuses Redis connection pools when subgraph-level configuration resolves to the same Redis configuration as the global all setting. Previously, the router could create redundant Redis connections even when the effective configuration was identical.

Impact: If you configure response caching at both the global and subgraph levels, you should see fewer Redis connections and lower connection overhead.

By @bnjjj in #8764

Prevent TLS connections from hanging when a handshake stalls (PR #8779)

The router listener loop no longer blocks while waiting for a TLS handshake to complete. Use server.http.tls_handshake_timeout to control how long the router waits before terminating a connection (default: 10s).

By @rohan-b99 in #8779

Emit cardinality overflow metrics for more OpenTelemetry error formats (PR #8740)

The router now emits the apollo.router.telemetry.metrics.cardinality_overflow metric for additional OpenTelemetry cardinality overflow error formats.

By @bonnici in #8740

Propagate trace context on WebSocket upgrade requests (PR #8739)

The router now injects trace propagation headers into the initial HTTP upgrade request when it opens WebSocket connections to subgraphs. This preserves distributed trace continuity between the router and subgraph services.

Trace propagation happens during the HTTP handshake only. After the WebSocket connection is established, headers cannot be added to individual messages.

By @theJC in #8739

Stop query planning compute jobs when the parent task is canceled (PR #8741)

Query planning compute jobs now stop when cooperative cancellation cancels the parent task.

By @rohan-b99 in #8741

Reject invalidation requests with unknown fields (PR #8752)

The response cache invalidation endpoint now rejects request payloads that include unknown fields. When unknown fields are present, the router returns HTTP 400 (Bad Request).

By @bnjjj in #8752

Restore plugin access to SubscriptionTaskParams in execution::Request builders (PR #8771)

Plugins and other external crates can use SubscriptionTaskParams with execution::Request builders again. This restores compatibility for plugin unit tests that construct subscription requests.

By @aaronArinder in #8771

Support JWT tokens with multiple audiences (PR #8780)

When issuers or audiences is included in the router's JWK configuration, the router will check each request's JWT for iss or aud and reject requests with mismatches.

Expected behavior:

  • If present, the iss claim must be specified as a string.
    • ✅ The JWK's issuers is empty.
    • ✅ The iss is a string and is present in the JWK's issuers.
    • ✅ The iss is null.
    • ❌ The iss is a string but is not present in the JWK's issuers.
    • ❌ The iss is not a string or null.
  • If present, the aud claim can be specified as either a string or an array of strings.
    • ✅ The JWK's audiences is empty.
    • ✅ The aud is a string and is present in the JWK's audiences.
    • ✅ The aud is an array of strings and at least one of those strings is present in the JWK's audiences.
    • ❌ The aud is not a string or array of strings (i.e., null).

Behavior prior to this change:

  • If the iss was not null or a string, it was permitted (regardless of its value).
  • If the aud was an array, it was rejected (regardless of its value).

By @carodewig in #8780

Enforce feature restrictions for warning-state licenses (PR #8768)

The router now enforces license restrictions even when a license is in a warning state. Previously, warning-state licenses could bypass enforcement for restricted features.

If your deployment uses restricted features, the router returns an error instead of continuing to run.

By @aaronArinder in #8768

🛠 Maintenance

Warn at startup when OTEL_EXPORTER_OTLP_ENDPOINT is set (PR #8729)

The ...

Read more

v2.11.0-rc.0

22 Jan 16:55

Choose a tag to compare

v2.11.0-rc.0 Pre-release
Pre-release
2.11.0-rc.0

v2.11.0-abstract.1

18 Dec 20:08
v2.11.0-abstract.1
df42d72

Choose a tag to compare

v2.11.0-abstract.1 Pre-release
Pre-release
2.11.0-abstract.1

v2.10.0 (LTS)

12 Dec 12:42
696b1e1

Choose a tag to compare

Long-Term Support

This release is marked for LTS under the v2026.1 LTS Policy for the GraphOS Runtime. It will be supported until September 30, 2026 with patch updates.

🚀 Features

Response caching is now Generally Available 🎉 (PR #8678)

Response caching is now Generally Available (GA) and ready for production use!

Response caching enables the router to cache subgraph query responses using Redis, improving query latency and reducing load on your underlying services. Unlike traditional HTTP caching solutions, response caching provides GraphQL-aware caching at the entity and root field level, making cached data reusable across different users and queries.

For complete documentation, configuration options, and quickstart guide, see the response caching documentation.

Key benefits

  • Improved performance: Cache origin responses and reuse them across queries to reduce latency
  • Reduced subgraph load: Minimize redundant requests to your subgraphs by serving cached data
  • Entity-level caching: Cache individual entity representations independently, enabling fine-grained control over data freshness
  • Flexible cache control: Set different TTLs for different types of data based on @cacheControl directives or Cache-Control response headers
  • Privacy-aware: Share cached data across users while maintaining privacy for personalized data
  • Active cache invalidation: Tag cached data with @cacheTag and invalidate specific cache entries via HTTP endpoint when data changes

What's cached

The router caches two kinds of data:

  • Root query fields: Cached as complete units (the entire response for these root fields)
  • Entity representations: Cached independently—each origin's contribution to an entity is cached separately and can be reused across different queries

Additional features

  • Cache debugger: See exactly what's being cached during development
  • Redis cluster support: Scale your cache with Redis cluster deployments and read replicas
  • Comprehensive metrics: Monitor cache performance with detailed Redis-specific metrics

By @bnjjj in #8678

Support Redis read replicas (PR #8405)

Read-only queries are now sent to replica nodes when using clustered Redis. Previously, all commands were sent to the primary nodes.

This change applies to all Redis caches, including the query plan cache and the response cache.

By @carodewig in #8405

Enable HTTP/2 header size limits for TCP and UDS (PR #8673)

The router's HTTP/2 header size limit configuration option now applies to requests using TCP and UDS (Unix domain sockets). Previously, this setting only worked for TLS connections.

By @aaronArinder in #8673

🐛 Fixes

Enable invalidation endpoint when any subgraph has invalidation enabled (PR #8680)

Previously, the response cache invalidation endpoint was only enabled when global invalidation was enabled via response_cache.subgraph.all.invalidation.enabled. If you enabled invalidation for only specific subgraphs without enabling it globally, the invalidation endpoint wouldn't start, preventing cache invalidation requests from being processed.

The invalidation endpoint now starts if either:

  • Global invalidation is enabled (response_cache.subgraph.all.invalidation.enabled: true), OR
  • Any individual subgraph has invalidation enabled

This enables more flexible configuration where you can enable invalidation selectively for specific subgraphs:

response_cache:
  enabled: true
  invalidation:
    listen: 127.0.0.1:4000
    path: /invalidation
  subgraph:
    all:
      enabled: true
      # Global invalidation not enabled
    subgraphs:
      products:
        invalidation:
          enabled: true  # Endpoint now starts
          shared_key: 

By @bnjjj in #8680

Require Redis configuration only when response caching is enabled (PR #8684)

Previously, the router attempted to connect to Redis for response caching regardless of whether response caching was enabled or disabled. This caused unnecessary connection attempts and configuration errors even when the feature was explicitly disabled.

The router now ignores Redis configuration if response caching is disabled. If response caching is configured to be enabled, Redis configuration is required, and missing Redis configuration raises an error on startup:

Error: you must have a redis configured either for all subgraphs or for subgraph "products"

By @bnjjj in #8684

Prevent deleted coprocessor context keys from reappearing in later stages (PR #8679)

Coprocessor context keys deleted in a previous stage no longer reappear in later stages.

By @rohan-b99 in #8679

Customize response caching behavior at the subgraph level (PR #8652)

You can now customize cached responses using Rhai or coprocessors. You can also set a different private_id based on subgraph request headers.

Example Rhai script customizing private_id:

fn subgraph_service(service, subgraph) {
    service.map_request(|request| {
        if "private_id" in request.headers {
            request.context["private_id"] = request.headers["private_id"];
        }
    });
}

By @bnjjj in #8652

Prevent glibc mismatch in DIY Docker images (Issue #8450)

The DIY Dockerfile now pins the Rust builder to the Bookworm variant (for example, rust:1.91.1-slim-bookworm) so the builder and runtime share the same Debian base. This prevents the image from failing at startup with /lib/x86_64-linux-gnu/libc.so.6: version 'GLIBC_2.39' not found.

This resolves a regression introduced when the rust:1.90.0 bump used a generic Rust image without specifying a Debian variant. The upstream Rust image default advanced to a newer variant with glibc 2.39, although the DIY runtime remained on Bookworm, creating a version mismatch.

By @theJC in #8629

Correct response cache fetch error metric (PR #8711)

The apollo.router.operations.response_cache.fetch.error metric was out of sync with the apollo.router.cache.redis.errors metric because errors weren't being returned from the Redis client wrapper. The response caching plugin now increments the error metric as expected.

By @carodewig in #8711

Emit http.client.request.body.size metric correctly (PR #8712)

The histogram for http.client.request.body.size was using the SubgraphRequestHeader selector, looking for Content-Length before it had been set in on_request, so http.client.request.body.size wasn't recorded. The router now uses the on_response handler and stores the body size in the request context extensions.

By @rohan-b99 in #8712

Record http.server.response.body.size metric correctly (PR #8697)

Previously, the http.server.response.body.size metric wasn't recorded because the router attempted to read from the Content-Length header before it had been set. The router now uses the size_hint of the body if it's exact.

By @rohan-b99 in #8697

Treat interface objects as entities in response caching (PR #8582)

Interface objects can be entities, but response caching wasn't treating them that way. Interface objects are now respected as entities so they can be used as cache keys.

By @aaronArinder in #8582

🛠 Maintenance

Warn when Datadog propagator isn't exclusively active (PR #8677)

The router now validates propagator configuration and emits a warning log if:

  • The Datadog propagator is enabled and any other propagators are enabled (except baggage)
  • Datadog tracing is enabled and other propagators are enabled (except baggage)

By @rohan-b99 in #8677

v2.10.0-rc.0

08 Dec 17:39

Choose a tag to compare

v2.10.0-rc.0 Pre-release
Pre-release
2.10.0-rc.0

v2.9.0

28 Nov 17:47
f65c6b9

Choose a tag to compare

🚀 Features

Add CORS Private Network Access support (PR #8279)

CORS configuration now supports private network access (PNA). Enable PNA for a CORS policy by specifying the private_network_access field, which supports two optional subfields: access_id and access_name.

Example configuration:

cors:
  policies:
    - origins: ["https://studio.apollographql.com"]
      private_network_access:
        access_id:
    - match_origins: ["^https://(dev|staging|www)?\\.my-app\\.(com|fr|tn)$"]
      private_network_access:
        access_id: "01:23:45:67:89:0A"
        access_name: "mega-corp device"

By @TylerBloom in #8279

Configure maximum HTTP/2 header list size (PR #8636)

The router now supports configuring the maximum size for HTTP/2 header lists via the limits.http2_max_headers_list_bytes setting. This protects against excessive resource usage from clients sending large sets of HTTP/2 headers.

The default remains 16KiB. When a client sends a request with HTTP/2 headers whose total size exceeds the configured limit, the router rejects the request with a 431 error code.

Example configuration:

limits:
  http2_max_headers_list_bytes: "48KiB"

By @aaronArinder in #8636

Customize response cache key per subgraph via context (PR #8543)

The response cache key can now be customized per subgraph using the apollo::response_cache::key context entry. The new subgraphs field enables defining separate cache keys for individual subgraphs.

Subgraph-specific data takes precedence over data in the all field—the router doesn't merge them. To set common data when providing subgraph-specific data, add it to the subgraph-specific section.

Example payload:

{
  "all": 1,
  "subgraph_operation1": "key1",
  "subgraph_operation2": {
    "data": "key2"
  },
  "subgraphs": {
    "my_subgraph": {
      "locale": "be"
    }
  }
}

By @bnjjj in #8543

Add telemetry selector for Cache-Control metrics (PR #8524)

The new response_cache_control selector enables telemetry metrics based on the computed Cache-Control header from subgraph responses.

Example configuration:

telemetry:
  exporters:
    metrics:
      common:
        service_name: apollo-router
        views:
          - name: subgraph.response.cache_control.max_age
            aggregation:
              histogram:
                buckets:
                - 10
                - 100
                - 1000
                - 10000
                - 100000
  instrumentation:
    instruments:
      subgraph:
        subgraph.response.cache_control.max_age:
          value:
            response_cache_control: max_age
          type: histogram
          unit: s
          description: A histogram of the computed TTL for a subgraph response

By @bnjjj in #8524

🐛 Fixes

Remove _redacted suffix from event attributes in apollo.router.state.change.total metric (Issue #8464)

Event names in the apollo.router.state.change.total metric no longer include the _redacted suffix. The metric now uses the Display trait instead of Debug for event names, changing values like updateconfiguration_redacted to updateconfiguration in APM platforms.

The custom behavior for UpdateLicense events is retained—the license state name is still appended.

By @rohan-b99 in #8464

Preserve Content-Length header for responses with known size (Issue #7941)

The router now uses the Content-Length header for GraphQL responses with known content lengths instead of transfer-encoding: chunked. Previously, the fleet_detector plugin destroyed HTTP body size hints when collecting metrics.

This extends the fix from #6538, which preserved size hints for router → subgraph requests, to also cover client → router requests and responses. Size hints now flow correctly through the entire pipeline for optimal HTTP header selection.

By @morriswchris in #7977

Correct apollo.router.operations.subscriptions.events metric counting (PR #8483)

The apollo.router.operations.subscriptions.events metric now increments correctly for each subscription event (excluding ping/pong/close messages). The counter call has been moved into the stream to trigger on each event.

This change also removes custom pong response handling before connection acknowledgment, which previously caused duplicate pongs because the WebSocket implementation already handles pings by default.

By @rohan-b99 in #8483

Unify timeout codes in response caching metrics (PR #8515)

Tokio- and Redis-based timeouts now use the same timeout code in apollo.router.operations.response_cache.*.error metrics. Previously, they were inadvertently given different code values.

By @carodewig in #8515

📃 Configuration

Remove unused TTL parameter from response cache Redis configuration (PR #8513)

The ttl parameter under redis configuration had no effect and is removed. Configure TTL at the subgraph level to control cache entry expiration:

preview_response_cache:
  enabled: true
  subgraph:
    all:
      enabled: true
      ttl: 10m  # ✅ Configure TTL here
      redis:
        urls: [ "redis://..." ]
        # ❌ ttl was here previously (unused)

By @carodewig in #8513

📚 Documentation

Document active subgraph requests selector (PR #8530)

The telemetry selectors documentation now correctly reflects the active_subgraph_requests attribute.

By @faisalwaseem in #8530

Add Redis cache suggestions to response cache documentation (PR #8624)

The FAQ now includes information about supported Redis versions and Redis key eviction setup.

By @carodewig in #8624

v2.9.0-rc.4

21 Nov 22:27

Choose a tag to compare

v2.9.0-rc.4 Pre-release
Pre-release
2.9.0-rc.4

v2.9.0-rc.3

21 Nov 17:50

Choose a tag to compare

v2.9.0-rc.3 Pre-release
Pre-release
2.9.0-rc.3

v2.9.0-rc.1

21 Nov 13:33

Choose a tag to compare

v2.9.0-rc.1 Pre-release
Pre-release
2.9.0-rc.1

v2.9.0-rc.0

21 Nov 11:40

Choose a tag to compare

v2.9.0-rc.0 Pre-release
Pre-release
2.9.0-rc.0