Apollo Router Changelogs


This page contains the changelog for the latest release of Apollo Router.

Go to GitHub to view changelogs for all router releases.

v2.5.0

πŸš€ Features

Introduce per-origin CORS policies (PR #7853)

Configuration can now specify different Cross-Origin Resource Sharing (CORS) rules for different origins using the cors.policies key. See the CORS documentation for details.

cors:
  policies:
    # The default CORS options work for Studio.
    - origins: ["https://studio.apollographql.com"]
    # Specific config for trusted origins
    - match_origins: ["^https://(dev|staging|www)?\\.my-app\\.(com|fr|tn)$"]
      allow_credentials: true
      allow_headers: ["content-type", "authorization", "x-web-version"]
    # Catch-all for untrusted origins
    - origins: ["*"]
      allow_credentials: false
      allow_headers: ["content-type"]

jemalloc metrics (PR #7735)

This PR adds the following new metrics when running the router on Linux with its default global-allocator feature:

πŸ› Fixes

Coprocessor: improve handling of invalid GraphQL responses with conditional validation (PR #7731)

The router was creating invalid GraphQL responses internally, especially when subscriptions terminate. When a coprocessor is configured, it validates all responses for correctness, causing errors to be logged when the router generates invalid internal responses. This affects the reliability of subscription workflows with coprocessors.

Fix handling of invalid GraphQL responses returned from coprocessors, particularly when used with subscriptions. Added conditional response validation and improved testing to ensure correctness. Added the response_validation configuration option at the coprocessor level to enable the response validation (by default it's enabled).

Fix deduplicated subscriptions hanging when one subscription closes (PR #7879)

Fixes a regression introduced in v1.50.0. When multiple client subscriptions are deduped onto a single subgraph subscription in WebSocket passthrough mode, and the first client subscription closes, the Router would close the subgraph subscription. The other deduplicated subscriptions would then silently stop receiving events.

Now outgoing subscriptions to subgraphs are kept open as long as any client subscription uses them.

Fix several hot reload issues with subscriptions (PR #7746)

When a hot reload is triggered by a configuration change, the router attempted to apply updated configuration to open subscriptions. This could cause excessive logging.

When a hot reload was triggered by a schema change, the router closed subscriptions with a SUBSCRIPTION_SCHEMA_RELOAD error. This happened before the new schema was fully active and warmed up, so clients could reconnect to the old schema, which should not happen.

To fix these issues, a configuration and a schema change now have the same behavior. The router waits for the new configuration and schema to be active, and then closes all subscriptions with a SUBSCRIPTION_SCHEMA_RELOAD/SUBSCRIPTION_CONFIG_RELOAD error, so clients can reconnect.

Fix error when removing non-UTF-8 headers with Rhai plugin (PR #7801)

When trying to remove non-UTF-8 headers from a Rhai plugin, users were faced with an unhelpful error. Now, non-UTF-8 values will be lossy converted to UTF-8 when accessed from Rhai. This change affects get, get_all, and remove operations.

Query planning errors with progressive override on interface implementations (PR #7929)

The router now correctly generates query plans when using progressive override (@override with labels) on types that implement interfaces within the same subgraph. Previously, the Rust query planner would fail to generate plans for these scenarios with the error "Was not able to find any options for {}: This shouldn't have happened.", while the JavaScript planner handled them correctly.

This fix resolves planning failures when your schema uses:

  • Interface implementations local to a subgraph
  • Progressive override directives on both the implementing type and its fields
  • Queries that traverse through the overridden interface implementations

The router will now successfully plan and execute queries that previously resulted in query planning errors.

Fix startup hang with an empty Persisted Queries list (PR #7831)

When the Persisted Queries feature is enabled, the router no longer hangs during startup when using a GraphOS account with no Persisted Queries manifest.

Remove @ from error paths (Issue #4548)

When a subgraph returns an unexpected response (ie not a body with at least one of errors or data), the errors surfaced by the router include an @ in the path which indicates an error applied to all elements in the array. This is not a behavior defined in the GraphQL spec and is not easily parsed.

This fix expands the @ symbol to reflect all paths that the error applies to.

Example

Consider a federated graph with two subgraphs, products and inventory, and a topProducts query which fetches a list of products from products and then fetches an inventory status for each product.

A successful response might look like:

{
    "data": {
        "topProducts": [
            {"name": "Table", "inStock": true},
            {"name": "Chair", "inStock": false}
        ]
    }
}

Prior to this change, if the inventory subgraph returns a malformed response, the router response would look like:

{
    "data": {"topProducts": [{"name": "Table", "inStock": null}, {"name": "Chair", "inStock": null}]}, 
    "errors": [
        {
            "message": "service 'inventory' response was malformed: graphql response without data must contain at least one error", 
            "path": ["topProducts", "@"], 
            "extensions": {"service": "inventory", "reason": "graphql response without data must contain at least one error", "code": "SUBREQUEST_MALFORMED_RESPONSE"}
        }
    ]
}

With this change, the response will look like:

{
    "data": {"topProducts": [{"name": "Table", "inStock": null}, {"name": "Chair", "inStock": null}]},
    "errors": [
        {
            "message": "service 'inventory' response was malformed: graphql response without data must contain at least one error",
            "path": ["topProducts", 0],
            "extensions": {"service": "inventory", "reason": "graphql response without data must contain at least one error", "code": "SUBREQUEST_MALFORMED_RESPONSE"}
        },
        {
            "message": "service 'inventory' response was malformed: graphql response without data must contain at least one error",
            "path": ["topProducts", 1],
            "extensions": {"service": "inventory", "reason": "graphql response without data must contain at least one error", "code": "SUBREQUEST_MALFORMED_RESPONSE"}
        }
    ]
}

The above examples reflect the behavior with include_subgraph_errors = true; if include_subgraph_errors is false:

{
    "data": {"topProducts": [{"name": "Table", "inStock": null}, {"name": "Chair", "inStock": null}]},
    "errors": [
        {
            "message": "Subgraph errors redacted",
            "path": ["topProducts", 0]
        },
        {
            "message": "Subgraph errors redacted",
            "path": ["topProducts", 1]
        }
    ]
}

Remove use of APOLLO_TELEMETRY_DISABLED from the fleet detector plugin (PR #7907)

The APOLLO_TELEMETRY_DISABLED environment variable only disables anonymous telemetry, it was never meant for disabling identifiable telemetry. This includes metrics from the fleet detection plugin.

Feedback

Edit on GitHub

Ask Community