From 4c8511105c9bd89bfef028e6d6147f1f7091552d Mon Sep 17 00:00:00 2001 From: Michelle Mabuyo Date: Mon, 2 Feb 2026 17:35:11 -0500 Subject: [PATCH 1/3] redirect to one canonical source for PQ setup, remove duplicated information --- .../routing/security/persisted-queries.mdx | 197 +----------------- 1 file changed, 2 insertions(+), 195 deletions(-) diff --git a/docs/source/routing/security/persisted-queries.mdx b/docs/source/routing/security/persisted-queries.mdx index f82abc2e20..478afce793 100644 --- a/docs/source/routing/security/persisted-queries.mdx +++ b/docs/source/routing/security/persisted-queries.mdx @@ -12,199 +12,6 @@ Developer and Standard plans require Router v2.6.0 or later. - +Persisted queries let you maintain an allowlist of trusted operations and optionally require clients to send operation IDs instead of full operation strings. The GraphOS Router enforces safelisting and fetches the persisted query list (PQL) from GraphOS. -## Differences from automatic persisted queries - -The Apollo Router Core also supports a related feature called [automatic persisted queries](/router/configuration/in-memory-caching#caching-automatic-persisted-queries-apq) (APQ). With APQ, clients can execute a GraphQL operation by sending the SHA256 hash of its operation string instead of the entire string. **APQ doesn't support safelisting** because the router updates its APQ cache over time with _any_ operations it receives. - -For more details on differences between APQ and this feature, see the [GraphOS persisted queries documentation](/graphos/operations/persisted-queries#differences-from-automatic-persisted-queries). - -## Implementation - -Enabling operation safelisting has a few steps: - -1. PQL creation and linking -1. Router configuration -1. Operation registration -1. Client updates - -This article details the router configuration step. -For more information on other configuration aspects, see the [GraphOS persisted queries documentation](/graphos/operations/persisted-queries). - -## Router configuration - - - -### Router security levels - - - -### Configuration options - -The router provides four configuration options that you can combine to create the recommended [security levels](#router-security-levels). This section details each configuration option. Refer to the [security levels](#router-security-levels) section for recommended combinations. - - - -From version `1.25.0` to `1.32.0`, the `persisted_queries` configuration option was named `preview_persisted_queries`. Upgrade your router to version `1.32.0` or later to use the [generally available](/resources/product-launch-stages/#general-availability) version of the feature and the example configuration snippets below. - - - -#### `persisted_queries` - - - -This base configuration enables the feature. All other configuration options build off this one. - -```yaml title="router.yaml" -persisted_queries: - enabled: true -``` - -#### `log_unknown` - - - -Adding `log_unknown: true` to `persisted_queries` configures the router to log any incoming operations not registered to the PQL. - -```yaml title="router.yaml" -persisted_queries: - enabled: true - log_unknown: true -``` - -If used with the [`safelist`](#safelist) option, the router logs unregistered and rejected operations. With [`safelist.require_id`](#require_id) off, the only rejected operations are unregistered ones. If [`safelist.require_id`](#require_id) is turned on, operations can be rejected even when registered because they use operation IDs rather than operation strings. - -#### `experimental_prewarm_query_plan_cache` - -
- - -
- -By default, the router [prewarms the query plan cache](/router/configuration/in-memory-caching#cache-warm-up) using all operations on the PQL when a new schema is loaded, but not at startup. Using the `experimental_prewarm_query_plan_cache` option, you can tell the router to prewarm the cache using the PQL on startup as well, or tell it not to prewarm the cache when reloading the schema. (This does not affect whether the router prewarms the query plan cache with recently-used operations from its in-memory cache.) Prewarming the cache can reduce request latency by ensuring that operations are pre-planned when requests are received, but can make startup or schema reloads slower. - -```yaml title="router.yaml" -persisted_queries: - enabled: true - experimental_prewarm_query_plan_cache: - on_startup: true # default: false - on_reload: false # default: true -``` - -#### `local_manifests` - - - - - -From version `1.50.0` to `1.54`, the `local_manifests` configuration option was named `experimental_local_manifests`. Upgrade your router to version `1.55.0` or later to use the [generally available](/resources/product-launch-stages/#general-availability) version of the feature and the example configuration snippet below. - - - -Adding `local_manifests` to your `persisted-queries` configuration lets you use local persisted query manifests instead of the hosted Uplink version. This is helpful when you're using an [offline Enterprise license](/graphos/routing/license/#offline-license) and can't use Uplink. With `local_manifests`, the router doesn't reload the manifest from the file system, so you need to restart the router to apply changes. - -```yaml title="router.yaml" -persisted_queries: - enabled: true - local_manifests: - - ./path/to/persisted-query-manifest.json -``` - -You can download a version of your manifest to use locally from [GraphOS Studio](https://studio.apollographql.com/?referrer=docs-content). Open the PQL page for a graph by clicking the **Go to persisted query lists** to the left of the graph's name. Then, click the ••• menu under the **Actions** column to download a PQL's manifest as a JSON file. Save this file locally and update your `local_manifests` configuration with the path the file. - -#### `hot_reload` - - - - - -This option only works in tandem with the `local_manifests` option. - - - -If you configure `local_manifests`, you can set `hot_reload` to `true` to automatically reload manifest files whenever they change. This lets you update local manifest files without restarting the router. - -```yaml title="router.yaml" -persisted_queries: - enabled: true - local_manifests: - - ./path/to/persisted-query-manifest.json - hot_reload: true -``` - -#### `safelist` - - - -Adding `safelist: true` to `persisted_queries` causes the router to reject any operations that haven't been registered to your PQL. - -```yaml title="router.yaml" -persisted_queries: - enabled: true - safelist: - enabled: true -apq: - enabled: false -``` - - - -To enable safelisting, you _must_ turn off [automatic persisted queries](/router/configuration/in-memory-caching#caching-automatic-persisted-queries-apq) (APQs). APQs let clients [register arbitrary operations at runtime](/graphos/operations/persisted-queries/#differences-from-automatic-persisted-queries) while safelisting restricts operations to those that have been explicitly registered. - - - -By default, the [`require_id`](#require_id) suboption is `false`, meaning the router accepts both operation IDs and operation strings as long as the operation is registered. - -#### `require_id` - - - -Adding `require_id: true` to the `safelist` option causes the router to reject any operations that either: - -- haven't been registered to your PQL -- use a full operation string rather than the operation ID - -```yaml title="router.yaml" -persisted_queries: - enabled: true - safelist: - enabled: true - require_id: true -apq: - enabled: false -``` - - - -To enable safelisting, you _must_ turn off [automatic persisted queries](/router/configuration/in-memory-caching#caching-automatic-persisted-queries-apq) (APQs). APQs let clients [register arbitrary operations at runtime](/graphos/operations/persisted-queries/#differences-from-automatic-persisted-queries) while safelisting restricts operations to those that have been explicitly registered. - - - -### Customization via request context - -GraphOS Router can be [customized](/graphos/routing/customization/overview) via several mechanisms such as [Rhai scripts](/graphos/routing/customization/rhai) and [coprocessors](/graphos/routing/customization/coprocessor). These plugins can affect your router's persistent query processing by writing to the request context. - -#### `apollo_persisted_queries::client_name` - -When publishing operations to a PQL, you can specify a client name associated with the operation (by including a `clientName` field in the individual operation in your [manifest](/graphos/platform/security/persisted-queries#per-operation-properties), or by including the `--for-client-name` option to `rover persisted-queries publish`). If an operation has a client name, it will only be executed by requests that specify that client name. (Your PQL can contain multiple operations with the same ID and different client names.) - -Your customization (Rhai script, coprocessor, etc) can examine a request during the [Router Service stage](/graphos/routing/customization/overview#request-path) of the request path and set the `apollo_persisted_queries::client_name` value in the request context to the request's client name. - -If this context value is not set by a customization, your router will use the same client name used for [client awareness](/graphos/routing/observability/client-awareness) in observability. This client name is read from an HTTP header specified by `telemetry.apollo.client_name_header`, or `apollographql-client-name` by default. - -If your request specifies an ID and a client name but there is no operation in the PQL with that ID and client name, your router will look to see if there is an operation with that ID and no client name specified, and use that if it finds it. - -#### `apollo_persisted_queries::safelist::skip_enforcement` - -If safelisting is enabled, you can still opt out of safelist enforcement on a per-request basis. - -Your customization (Rhai script, coprocessor, etc) can examine a request during the [Router Service stage](/graphos/routing/customization/overview#request-path) of the request path and set the `apollo_persisted_queries::safelist::skip_enforcement` value in the request context to the boolean value `true`. - -For any request where you set this value, Router will skip safelist enforcement: requests with a full operation string will be allowed even if they are not in the safelist, and even if [`safelist.required_id`](#require_id) is enabled. - -This does not affect the behavior of the [`log_unknown` option](#log_unknown): unknown operations will still be logged if that option is set. - -## Limitations - -- **Unsupported with offline license**. A GraphOS Router using an [offline license](/graphos/routing/license/#offline-license) cannot use safelisting with persisted queries. The feature relies on Apollo Uplink to fetch persisted query manifests, so it doesn't work as designed when the router is disconnected from Uplink. +For the complete implementation guide, including PQL creation, operation registration, client updates, and incremental adoption, see [Safelisting with Persisted Queries](/graphos/platform/security/persisted-queries) in the GraphOS Platform docs. \ No newline at end of file From 4dbe3b5c027bb3d246c04aef7fe869b31721da7a Mon Sep 17 00:00:00 2001 From: Michelle Mabuyo Date: Mon, 2 Feb 2026 17:36:45 -0500 Subject: [PATCH 2/3] fix: links to pq page --- docs/source/routing/configuration/yaml.mdx | 2 +- docs/source/routing/query-planning/caching.mdx | 2 +- docs/source/routing/security/index.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/routing/configuration/yaml.mdx b/docs/source/routing/configuration/yaml.mdx index c276190c7c..0b58bb40ea 100644 --- a/docs/source/routing/configuration/yaml.mdx +++ b/docs/source/routing/configuration/yaml.mdx @@ -232,7 +232,7 @@ If you need to override the subgraph URL at runtime on a per-request basis, you You can enhance your graph's security with GraphOS Router by maintaining a persisted query list (PQL), an operation safelist made by your first-party apps. As opposed to automatic persisted queries (APQ) where operations are automatically cached, operations must be preregistered to the PQL. Once configured, the router checks incoming requests against the PQL. -Learn more in [Safelisting with persisted queries](/router/configuration/persisted-queries). +Learn more in [Safelisting with persisted queries](/graphos/platform/security/persisted-queries). --- diff --git a/docs/source/routing/query-planning/caching.mdx b/docs/source/routing/query-planning/caching.mdx index 2af284c95a..6d6e5954eb 100644 --- a/docs/source/routing/query-planning/caching.mdx +++ b/docs/source/routing/query-planning/caching.mdx @@ -51,7 +51,7 @@ supergraph: warmed_up_queries: 100 ``` -In addition, the router can use the contents of the [persisted query list](/graphos/routing/security/persisted-queries) to prewarm the cache. By default, it does this when loading a new schema but not on startup; you can [configure](/graphos/routing/security/persisted-queries#experimental_prewarm_query_plan_cache) it to change either of these defaults. +In addition, the router can use the contents of the [persisted query list](/graphos/platform/security/persisted-queries) to prewarm the cache. By default, it does this when loading a new schema but not on startup; you can [configure](/graphos/platform/security/persisted-queries#experimental_prewarm_query_plan_cache) it to change either of these defaults. #### Cache warm-up with headers diff --git a/docs/source/routing/security/index.mdx b/docs/source/routing/security/index.mdx index e69edf4ff5..11e303c093 100644 --- a/docs/source/routing/security/index.mdx +++ b/docs/source/routing/security/index.mdx @@ -10,7 +10,7 @@ Its security features contribute to a defense-in-depth approach, where different The features covered in this section include: - [**Authorization**](/graphos/routing/security/authorization) - define authorized access to GraphQL fields and types by annotating schemas with authorization primitives -- [**Persisted Queries**](/graphos/routing/security/persisted-queries) - configure the router to allow clients to register and persist cached lists of safe GraphQL queries and operations +- [**Persisted Queries**](/graphos/platform/security/persisted-queries) - configure the router to allow clients to register and persist cached lists of safe GraphQL queries and operations - [**Best Practices**](/graphos/platform/security/overview) - best practices for securing supergraphs - [**CORS**](/graphos/routing/security/cors) - control router access from browser-based clients - [**CSRF Prevention**](/graphos/routing/security/csrf) - configure cross-site request forgery (CSRF) prevention in the router From d2aa9450221b14262aa22b907c1eb9e37fad29cc Mon Sep 17 00:00:00 2001 From: Michelle Mabuyo Date: Tue, 3 Feb 2026 11:05:39 -0500 Subject: [PATCH 3/3] AI review comments --- docs/source/routing/configuration/yaml.mdx | 2 +- docs/source/routing/query-planning/caching.mdx | 2 +- docs/source/routing/security/index.mdx | 2 +- docs/source/routing/security/persisted-queries.mdx | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/routing/configuration/yaml.mdx b/docs/source/routing/configuration/yaml.mdx index 0b58bb40ea..6182b956c7 100644 --- a/docs/source/routing/configuration/yaml.mdx +++ b/docs/source/routing/configuration/yaml.mdx @@ -232,7 +232,7 @@ If you need to override the subgraph URL at runtime on a per-request basis, you You can enhance your graph's security with GraphOS Router by maintaining a persisted query list (PQL), an operation safelist made by your first-party apps. As opposed to automatic persisted queries (APQ) where operations are automatically cached, operations must be preregistered to the PQL. Once configured, the router checks incoming requests against the PQL. -Learn more in [Safelisting with persisted queries](/graphos/platform/security/persisted-queries). +Learn more about [safelisting with persisted queries](/graphos/platform/security/persisted-queries). --- diff --git a/docs/source/routing/query-planning/caching.mdx b/docs/source/routing/query-planning/caching.mdx index 6d6e5954eb..adab6932fb 100644 --- a/docs/source/routing/query-planning/caching.mdx +++ b/docs/source/routing/query-planning/caching.mdx @@ -51,7 +51,7 @@ supergraph: warmed_up_queries: 100 ``` -In addition, the router can use the contents of the [persisted query list](/graphos/platform/security/persisted-queries) to prewarm the cache. By default, it does this when loading a new schema but not on startup; you can [configure](/graphos/platform/security/persisted-queries#experimental_prewarm_query_plan_cache) it to change either of these defaults. +Additionally, the router can use the [persisted query list](/graphos/platform/security/persisted-queries) to prewarm the cache. By default, the router prewarms the cache when loading a new schema but not on startup. You can [configure](/graphos/platform/security/persisted-queries#experimental_prewarm_query_plan_cache) the router to change these defaults. #### Cache warm-up with headers diff --git a/docs/source/routing/security/index.mdx b/docs/source/routing/security/index.mdx index 11e303c093..f1340ae910 100644 --- a/docs/source/routing/security/index.mdx +++ b/docs/source/routing/security/index.mdx @@ -10,7 +10,7 @@ Its security features contribute to a defense-in-depth approach, where different The features covered in this section include: - [**Authorization**](/graphos/routing/security/authorization) - define authorized access to GraphQL fields and types by annotating schemas with authorization primitives -- [**Persisted Queries**](/graphos/platform/security/persisted-queries) - configure the router to allow clients to register and persist cached lists of safe GraphQL queries and operations +- [**Persisted Queries**](/graphos/platform/security/persisted-queries) - configure the router to enable clients to register and persist lists of safe GraphQL queries and operations - [**Best Practices**](/graphos/platform/security/overview) - best practices for securing supergraphs - [**CORS**](/graphos/routing/security/cors) - control router access from browser-based clients - [**CSRF Prevention**](/graphos/routing/security/csrf) - configure cross-site request forgery (CSRF) prevention in the router diff --git a/docs/source/routing/security/persisted-queries.mdx b/docs/source/routing/security/persisted-queries.mdx index 478afce793..419ea9dbae 100644 --- a/docs/source/routing/security/persisted-queries.mdx +++ b/docs/source/routing/security/persisted-queries.mdx @@ -12,6 +12,6 @@ Developer and Standard plans require Router v2.6.0 or later. -Persisted queries let you maintain an allowlist of trusted operations and optionally require clients to send operation IDs instead of full operation strings. The GraphOS Router enforces safelisting and fetches the persisted query list (PQL) from GraphOS. +Persisted queries enable you to maintain an allowlist of trusted operations and optionally require clients to send operation IDs instead of full operation strings. The GraphOS Router enforces safelisting and fetches the persisted query list (PQL) from GraphOS. -For the complete implementation guide, including PQL creation, operation registration, client updates, and incremental adoption, see [Safelisting with Persisted Queries](/graphos/platform/security/persisted-queries) in the GraphOS Platform docs. \ No newline at end of file +For the complete implementation guide, including PQL creation, operation registration, client updates, and incremental adoption, see [Safelisting with Persisted Queries](/graphos/platform/security/persisted-queries). \ No newline at end of file