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
2 changes: 1 addition & 1 deletion docs/source/routing/configuration/yaml.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 about [safelisting with persisted queries](/graphos/platform/security/persisted-queries).

---

Expand Down
2 changes: 1 addition & 1 deletion docs/source/routing/query-planning/caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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

Expand Down
2 changes: 1 addition & 1 deletion docs/source/routing/security/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
Expand Down
197 changes: 2 additions & 195 deletions docs/source/routing/security/persisted-queries.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,199 +12,6 @@ Developer and Standard plans require Router v2.6.0 or later.

</PlanRequired>

<PQIntro />
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.

## 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

<PQRouterConfiguration />

### Router security levels

<PQSecurityLevels />

### 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.

<Note>

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.

</Note>

#### `persisted_queries`

<MinVersionBadge version="Router v1.32.0" />

This base configuration enables the feature. All other configuration options build off this one.

```yaml title="router.yaml"
persisted_queries:
enabled: true
```

#### `log_unknown`

<MinVersionBadge version="Router v1.32.0" />

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`

<div className="flex flex-row items-start gap-2 mt-2">
<MinVersionBadge version="Router v1.55.0" />
<ExperimentalFeatureBadge />
</div>

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`

<MinVersionBadge version="Router v1.55.0" />

<Note>

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.

</Note>

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`

<MinVersionBadge version="Router v2.1.0" />

<Note>

This option only works in tandem with the `local_manifests` option.

</Note>

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`

<MinVersionBadge version="Router v1.32.0" />

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
```

<Note>

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.

</Note>

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`

<MinVersionBadge version="Router v1.32.0" />

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
```

<Note>

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.

</Note>

### 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).