Skip to content

Persisted Documents#868

Open
kamilkisiela wants to merge 1 commit intomainfrom
kamil-persisted-documents
Open

Persisted Documents#868
kamilkisiela wants to merge 1 commit intomainfrom
kamil-persisted-documents

Conversation

@kamilkisiela
Copy link
Copy Markdown
Contributor

@kamilkisiela kamilkisiela commented Mar 20, 2026

This PR introduces Persisted Documents support with configurable extraction and storage, plus lot of e2e tests.

Closes #311


Documentation PR: graphql-hive/docs#76


Supports document ID extraction from:

  • documentId body field or URL query param (by default)
  • Apollo-style extensions.persistedQuery.sha256Hash (by default)
  • custom json_path (like doc_id or extensions.whatever.id
  • url_query_param (like ?doc_id=123)
  • url_path_param (like /graphql/:id)

In the example below, we first look for the path pattern and then the query param.

persisted_documents:
  extractors:
    - type: url_path_param
       template: /:id # relative to configured endpoint
    - type: url_query_param
       name: id # `?id=123

Supports different document storages:

  • file manifest (in Apollo and Key-Value Relay style formats)
  • Hive CDN (via hive-console-sdk)

File storage has watch mode by default (works well with relay-compiler --watch), so when a file changes (we debounce the events for 150ms) the document manifest is reloaded and served fresh.

Hive storage includes syntax validation of the provided document id. We make sure we don't send what str.replace('~', '/') produces to the Hive CDN without verification. If we do, people would see 404 with no info that doc id is incorrect.

Includes require_id: boolean to control whether to require requests with document id only or not.

Includes log_missing_id_requests: bool (false by default) that logs information about requests with no document id. Helpful if you migrate from regular to queryless requests.

Regarding Hive CDN. We don't rely only on appName~appVersion~documentId format of the document id, but app's name and version can be inferred from client identification headers (graphql-client-name etc - configurable via telemetry settings). We support it for reasons mentioned in the Slack Canvas doc (better DX and reusable clientAwarness feature of Apollo Client).

I also added two metrics to measure:

  • requests with no document id - so devs know that some requests still send no id
  • document resolution failures - so devs know that some requests with doc id that has no document text

Noteworthy implementation details

Persisted documents are implemented under pipeline/persisted_documents/* with clear split:

  • extraction (extract/*)
  • resolution (resolve/*)
  • runtime (mod.rs, types.rs)

Closes #867 - as I introduced single-flight resolution of documents in the SDK. The Err had to be cloanable (otherwise I would have to change the API to return Arc), so some error enum variants in the SDK was converted to String instead of raw errors from 3rd-party libraries.

I also added a negative cache to store non 2XX requests for 5s (configurable, but in SDK it's disabled by default) to not keep repeating the same requests that eventually give errors or 404s.

I cleaned up and moved the code responsible for preparation of graphql params, decoding of GET and POST payloads into GraphQLGetInput and GraphQLPostInput and OperationPreparation structs. This way the flow is clear, like what happens when we receive GET request, what when we receive POST, and how it's all translated to what the rest of the pipeline expects. It's in bin/router/src/pipeline/execution_request.rs.

I did bunch of tricks to make sure we're performant:

  • custom query param reader (based on memchr)
  • conditional extraction of non standard JSON fields (fields that are not query, extensions etc)
  • built-in extraction of documentId during deserialization
  • supafast validation of document ids (based on memchr)

There are many new lines of code, but majority is just e2e tests.

For reviewers, I recommend to check:

  • docs/persisted-documents to understand what I built and why
  • bin/router/src/pipeline/persisted_documents - pretty much everything related to persisted documents, how things are extracted, how documents are resolved
  • bin/router/src/pipeline/execution_request.rs - to understand how we convert POST and GET request into data consumed by the rest of the pipeline and this is when extraction and resolution of persisted documents happen.

Performance is identical as before (check persisted-documents bench in CI).

@gemini-code-assist

This comment was marked as outdated.

gemini-code-assist[bot]

This comment was marked as outdated.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 20, 2026

🐋 This PR was built and pushed to the following Docker images:

Image Names: ghcr.io/graphql-hive/router

Platforms: linux/amd64,linux/arm64

Image Tags: ghcr.io/graphql-hive/router:pr-868 ghcr.io/graphql-hive/router:sha-1ac2386

Docker metadata
{
"buildx.build.provenance/linux/amd64": {
  "builder": {
    "id": "https://github.com/graphql-hive/router/actions/runs/24070370021/attempts/1"
  },
  "buildType": "https://mobyproject.org/buildkit@v1",
  "materials": [
    {
      "uri": "pkg:docker/docker/dockerfile@1.22",
      "digest": {
        "sha256": "4a43a54dd1fedceb30ba47e76cfcf2b47304f4161c0caeac2db1c61804ea3c91"
      }
    },
    {
      "uri": "pkg:docker/gcr.io/distroless/cc-debian12@latest?platform=linux%2Famd64",
      "digest": {
        "sha256": "329e54034ce498f9c6b345044e8f530c6691f99e94a92446f68c0adf9baa8464"
      }
    }
  ],
  "invocation": {
    "configSource": {
      "entryPoint": "router.Dockerfile"
    },
    "parameters": {
      "frontend": "gateway.v0",
      "args": {
        "cmdline": "docker/dockerfile:1.22",
        "label:org.opencontainers.image.created": "2026-04-07T08:00:43.205Z",
        "label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
        "label:org.opencontainers.image.licenses": "MIT",
        "label:org.opencontainers.image.revision": "1ac238648612eef6ee22abdcede3f39ad64ac0e1",
        "label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
        "label:org.opencontainers.image.title": "router",
        "label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
        "label:org.opencontainers.image.vendor": "theguild",
        "label:org.opencontainers.image.version": "pr-868",
        "source": "docker/dockerfile:1.22"
      },
      "locals": [
        {
          "name": "context"
        },
        {
          "name": "dockerfile"
        }
      ]
    },
    "environment": {
      "github_actor": "kamilkisiela",
      "github_actor_id": "8167190",
      "github_event_name": "pull_request",
      "github_event_payload": {
        "action": "synchronize",
        "after": "c47dac4cfd7e6097b29b69f08a76c2b3af4212a0",
        "before": "38a2c117ba0c8b2473fff17e19b2ff4d68ddc41e",
        "enterprise": {
          "avatar_url": "https://avatars.githubusercontent.com/b/187753?v=4",
          "created_at": "2024-07-02T08:52:28Z",
          "description": "",
          "html_url": "https://github.com/enterprises/the-guild",
          "id": 187753,
          "name": "The Guild",
          "node_id": "E_kgDOAALdaQ",
          "slug": "the-guild",
          "updated_at": "2026-03-11T16:47:15Z",
          "website_url": "https://the-guild.dev/"
        },
        "number": 868,
        "organization": {
          "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
          "description": "Schema registry, analytics and gateway for GraphQL federation and other GraphQL APIs.",
          "events_url": "https://api.github.com/orgs/graphql-hive/events",
          "hooks_url": "https://api.github.com/orgs/graphql-hive/hooks",
          "id": 182742256,
          "issues_url": "https://api.github.com/orgs/graphql-hive/issues",
          "login": "graphql-hive",
          "members_url": "https://api.github.com/orgs/graphql-hive/members{/member}",
          "node_id": "O_kgDOCuRs8A",
          "public_members_url": "https://api.github.com/orgs/graphql-hive/public_members{/member}",
          "repos_url": "https://api.github.com/orgs/graphql-hive/repos",
          "url": "https://api.github.com/orgs/graphql-hive"
        },
        "pull_request": {
          "_links": {
            "comments": {
              "href": "https://api.github.com/repos/graphql-hive/router/issues/868/comments"
            },
            "commits": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/868/commits"
            },
            "html": {
              "href": "https://github.com/graphql-hive/router/pull/868"
            },
            "issue": {
              "href": "https://api.github.com/repos/graphql-hive/router/issues/868"
            },
            "review_comment": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}"
            },
            "review_comments": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/868/comments"
            },
            "self": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/868"
            },
            "statuses": {
              "href": "https://api.github.com/repos/graphql-hive/router/statuses/c47dac4cfd7e6097b29b69f08a76c2b3af4212a0"
            }
          },
          "active_lock_reason": null,
          "additions": 5966,
          "assignee": null,
          "assignees": [],
          "author_association": "CONTRIBUTOR",
          "auto_merge": null,
          "base": {
            "label": "graphql-hive:main",
            "ref": "main",
            "repo": {
              "allow_auto_merge": false,
              "allow_forking": true,
              "allow_merge_commit": false,
              "allow_rebase_merge": false,
              "allow_squash_merge": true,
              "allow_update_branch": true,
              "archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
              "archived": false,
              "assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
              "blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
              "branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
              "clone_url": "https://github.com/graphql-hive/router.git",
              "collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
              "comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
              "commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
              "compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
              "contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
              "contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
              "created_at": "2024-11-20T16:16:12Z",
              "default_branch": "main",
              "delete_branch_on_merge": true,
              "deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
              "description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
              "disabled": false,
              "downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
              "events_url": "https://api.github.com/repos/graphql-hive/router/events",
              "fork": false,
              "forks": 9,
              "forks_count": 9,
              "forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
              "full_name": "graphql-hive/router",
              "git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
              "git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
              "git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
              "git_url": "git://github.com/graphql-hive/router.git",
              "has_discussions": false,
              "has_downloads": true,
              "has_issues": true,
              "has_pages": false,
              "has_projects": false,
              "has_pull_requests": true,
              "has_wiki": false,
              "homepage": "https://the-guild.dev/graphql/hive/docs/router",
              "hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
              "html_url": "https://github.com/graphql-hive/router",
              "id": 891604244,
              "is_template": false,
              "issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
              "issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
              "issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
              "keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
              "labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
              "language": "Rust",
              "languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
              "license": {
                "key": "mit",
                "name": "MIT License",
                "node_id": "MDc6TGljZW5zZTEz",
                "spdx_id": "MIT",
                "url": "https://api.github.com/licenses/mit"
              },
              "merge_commit_message": "PR_TITLE",
              "merge_commit_title": "MERGE_MESSAGE",
              "merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
              "milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
              "mirror_url": null,
              "name": "router",
              "node_id": "R_kgDONSTNFA",
              "notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
              "open_issues": 65,
              "open_issues_count": 65,
              "owner": {
                "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
                "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
                "followers_url": "https://api.github.com/users/graphql-hive/followers",
                "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
                "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/graphql-hive",
                "id": 182742256,
                "login": "graphql-hive",
                "node_id": "O_kgDOCuRs8A",
                "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
                "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
                "repos_url": "https://api.github.com/users/graphql-hive/repos",
                "site_admin": false,
                "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/graphql-hive",
                "user_view_type": "public"
              },
              "private": false,
              "pull_request_creation_policy": "all",
              "pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
              "pushed_at": "2026-04-07T07:45:49Z",
              "releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
              "size": 6191,
              "squash_merge_commit_message": "PR_BODY",
              "squash_merge_commit_title": "PR_TITLE",
              "ssh_url": "git@github.com:graphql-hive/router.git",
              "stargazers_count": 81,
              "stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
              "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
              "subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
              "subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
              "svn_url": "https://github.com/graphql-hive/router",
              "tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
              "teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
              "topics": [
                "apollo-federation",
                "federation",
                "federation-gateway",
                "graphql",
                "graphql-federation",
                "router"
              ],
              "trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
              "updated_at": "2026-04-03T14:14:41Z",
              "url": "https://api.github.com/repos/graphql-hive/router",
              "use_squash_pr_title_as_default": true,
              "visibility": "public",
              "watchers": 81,
              "watchers_count": 81,
              "web_commit_signoff_required": false
            },
            "sha": "03b6d920b8d6669f1977401e3c47f6ffca2ad520",
            "user": {
              "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
              "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
              "followers_url": "https://api.github.com/users/graphql-hive/followers",
              "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
              "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
              "gravatar_id": "",
              "html_url": "https://github.com/graphql-hive",
              "id": 182742256,
              "login": "graphql-hive",
              "node_id": "O_kgDOCuRs8A",
              "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
              "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
              "repos_url": "https://api.github.com/users/graphql-hive/repos",
              "site_admin": false,
              "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
              "type": "Organization",
              "url": "https://api.github.com/users/graphql-hive",
              "user_view_type": "public"
            }
          },
          "body": "This PR introduces Persisted Documents support with configurable extraction and storage, plus lot of e2e tests.\r\n\r\nCloses #311\r\n\r\n---\r\n\r\nDocumentation PR: https://github.com/graphql-hive/docs/pull/76\r\n\r\n - Preview of [security/persisted-documents](https://dc3c070a-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/security/persisted-documents)\r\n - Preview of [configuration/persisted_documents](https://dc3c070a-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/configuration/persisted_documents)\r\n\r\n---\r\n\r\nSupports document ID extraction from:\r\n  - `documentId` body field or URL query param (by default)\r\n  - Apollo-style `extensions.persistedQuery.sha256Hash` (by default)\r\n  - custom `json_path` (like `doc_id` or `extensions.whatever.id`\r\n  - `url_query_param` (like `?doc_id=123`)\r\n  - `url_path_param` (like `/graphql/:id`)\r\n \r\n\r\nIn the example below, we first look for the path pattern and then the query param.\r\n```yaml\r\npersisted_documents:\r\n  extractors:\r\n    - type: url_path_param\r\n       template: /:id # relative to configured endpoint\r\n    - type: url_query_param\r\n       name: id # `?id=123\r\n```\r\n\r\nSupports different document storages:\r\n  - file manifest (in Apollo and Key-Value Relay style formats)\r\n  - Hive CDN (via `hive-console-sdk`)\r\n  \r\nFile storage has **watch mode** by default (works well with `relay-compiler --watch`), so when a file changes (we debounce the events for 150ms) the document manifest is reloaded and served fresh.\r\n\r\nHive storage includes syntax validation of the provided document id. We make sure we don't send what `str.replace('~', '/')` produces to the Hive CDN without verification. If we do, people would see 404 with no info that doc id is incorrect.\r\n\r\nIncludes `require_id: boolean` to control whether to require requests with document id only or not.\r\n\r\nIncludes `log_missing_id_requests: bool` (false by default) that logs information about requests with no document id. Helpful if you migrate from regular to queryless requests.\r\n\r\nRegarding Hive CDN. We don't rely only on `appName~appVersion~documentId` format of the document id, but app's name and version can be inferred from client identification headers (`graphql-client-name` etc - configurable via telemetry settings). We support it for reasons mentioned in the Slack Canvas doc (better DX and reusable `clientAwarness` feature of Apollo Client).\r\n\r\nI also added two metrics to measure:\r\n- requests with no document id - so devs know that some requests still send no id\r\n- document resolution failures - so devs know that some requests with doc id that has no document text\r\n\r\n## Noteworthy implementation details\r\n\r\nPersisted documents are implemented under `pipeline/persisted_documents/*` with clear split:\r\n  - extraction (`extract/*`)\r\n  - resolution (`resolve/*`)\r\n  - runtime (`mod.rs`, `types.rs`)\r\n\r\nCloses #867 - as I introduced single-flight resolution of documents in the SDK. The **Err had to be cloanable** (otherwise I would have to change the API to return Arc<Err>), so some error enum variants in the SDK was converted to `String` instead of raw errors from 3rd-party libraries.\r\n\r\nI also added a **negative cache** to store non 2XX requests for 5s (configurable, but in SDK it's disabled by default) to not keep repeating the same requests that eventually give errors or 404s.\r\n\r\nI cleaned up and moved the code responsible for preparation of graphql params, decoding of GET and POST payloads into `GraphQLGetInput` and `GraphQLPostInput` and `OperationPreparation` structs. This way the flow is clear, like what happens when we receive GET request, what when we receive POST, and how it's all translated to what the rest of the pipeline expects. It's in `bin/router/src/pipeline/execution_request.rs`.\r\n\r\nI did bunch of tricks to make sure we're performant:\r\n- custom query param reader (based on `memchr`)\r\n- conditional extraction of non standard JSON fields (fields that are not `query`, `extensions` etc)\r\n- built-in extraction of `documentId` during deserialization\r\n- supafast validation of document ids (based on `memchr`)\r\n\r\n---\r\n\r\nThere are many new lines of code, but majority is just e2e tests.\r\n\r\nFor reviewers, I recommend to check:\r\n- `docs/persisted-documents` to understand what I built and why\r\n- `bin/router/src/pipeline/persisted_documents` - pretty much everything related to persisted documents, how things are extracted, how documents are resolved\r\n- `bin/router/src/pipeline/execution_request.rs` - to understand how we convert POST and GET request into data consumed by the rest of the pipeline and this is when extraction and resolution of persisted documents happen.\r\n\r\nPerformance is identical as before (check `persisted-documents` bench in CI).",
          "changed_files": 60,
          "closed_at": null,
          "comments": 3,
          "comments_url": "https://api.github.com/repos/graphql-hive/router/issues/868/comments",
          "commits": 1,
          "commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/868/commits",
          "created_at": "2026-03-20T15:23:35Z",
          "deletions": 212,
          "diff_url": "https://github.com/graphql-hive/router/pull/868.diff",
          "draft": false,
          "head": {
            "label": "graphql-hive:kamil-persisted-documents",
            "ref": "kamil-persisted-documents",
            "repo": {
              "allow_auto_merge": false,
              "allow_forking": true,
              "allow_merge_commit": false,
              "allow_rebase_merge": false,
              "allow_squash_merge": true,
              "allow_update_branch": true,
              "archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
              "archived": false,
              "assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
              "blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
              "branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
              "clone_url": "https://github.com/graphql-hive/router.git",
              "collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
              "comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
              "commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
              "compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
              "contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
              "contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
              "created_at": "2024-11-20T16:16:12Z",
              "default_branch": "main",
              "delete_branch_on_merge": true,
              "deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
              "description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
              "disabled": false,
              "downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
              "events_url": "https://api.github.com/repos/graphql-hive/router/events",
              "fork": false,
              "forks": 9,
              "forks_count": 9,
              "forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
              "full_name": "graphql-hive/router",
              "git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
              "git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
              "git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
              "git_url": "git://github.com/graphql-hive/router.git",
              "has_discussions": false,
              "has_downloads": true,
              "has_issues": true,
              "has_pages": false,
              "has_projects": false,
              "has_pull_requests": true,
              "has_wiki": false,
              "homepage": "https://the-guild.dev/graphql/hive/docs/router",
              "hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
              "html_url": "https://github.com/graphql-hive/router",
              "id": 891604244,
              "is_template": false,
              "issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
              "issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
              "issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
              "keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
              "labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
              "language": "Rust",
              "languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
              "license": {
                "key": "mit",
                "name": "MIT License",
                "node_id": "MDc6TGljZW5zZTEz",
                "spdx_id": "MIT",
                "url": "https://api.github.com/licenses/mit"
              },
              "merge_commit_message": "PR_TITLE",
              "merge_commit_title": "MERGE_MESSAGE",
              "merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
              "milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
              "mirror_url": null,
              "name": "router",
              "node_id": "R_kgDONSTNFA",
              "notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
              "open_issues": 65,
              "open_issues_count": 65,
              "owner": {
                "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
                "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
                "followers_url": "https://api.github.com/users/graphql-hive/followers",
                "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
                "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/graphql-hive",
                "id": 182742256,
                "login": "graphql-hive",
                "node_id": "O_kgDOCuRs8A",
                "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
                "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
                "repos_url": "https://api.github.com/users/graphql-hive/repos",
                "site_admin": false,
                "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/graphql-hive",
                "user_view_type": "public"
              },
              "private": false,
              "pull_request_creation_policy": "all",
              "pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
              "pushed_at": "2026-04-07T07:45:49Z",
              "releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
              "size": 6191,
              "squash_merge_commit_message": "PR_BODY",
              "squash_merge_commit_title": "PR_TITLE",
              "ssh_url": "git@github.com:graphql-hive/router.git",
              "stargazers_count": 81,
              "stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
              "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
              "subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
              "subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
              "svn_url": "https://github.com/graphql-hive/router",
              "tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
              "teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
              "topics": [
                "apollo-federation",
                "federation",
                "federation-gateway",
                "graphql",
                "graphql-federation",
                "router"
              ],
              "trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
              "updated_at": "2026-04-03T14:14:41Z",
              "url": "https://api.github.com/repos/graphql-hive/router",
              "use_squash_pr_title_as_default": true,
              "visibility": "public",
              "watchers": 81,
              "watchers_count": 81,
              "web_commit_signoff_required": false
            },
            "sha": "c47dac4cfd7e6097b29b69f08a76c2b3af4212a0",
            "user": {
              "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
              "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
              "followers_url": "https://api.github.com/users/graphql-hive/followers",
              "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
              "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
              "gravatar_id": "",
              "html_url": "https://github.com/graphql-hive",
              "id": 182742256,
              "login": "graphql-hive",
              "node_id": "O_kgDOCuRs8A",
              "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
              "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
              "repos_url": "https://api.github.com/users/graphql-hive/repos",
              "site_admin": false,
              "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
              "type": "Organization",
              "url": "https://api.github.com/users/graphql-hive",
              "user_view_type": "public"
            }
          },
          "html_url": "https://github.com/graphql-hive/router/pull/868",
          "id": 3425522123,
          "issue_url": "https://api.github.com/repos/graphql-hive/router/issues/868",
          "labels": [],
          "locked": false,
          "maintainer_can_modify": false,
          "merge_commit_sha": "ee6b5ad2d1d8f6b463c21d5ef1d1863e5d1ce55f",
          "mergeable": null,
          "mergeable_state": "unknown",
          "merged": false,
          "merged_at": null,
          "merged_by": null,
          "milestone": null,
          "node_id": "PR_kwDONSTNFM7MLVHL",
          "number": 868,
          "patch_url": "https://github.com/graphql-hive/router/pull/868.patch",
          "rebaseable": null,
          "requested_reviewers": [],
          "requested_teams": [],
          "review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
          "review_comments": 19,
          "review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/868/comments",
          "state": "open",
          "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/c47dac4cfd7e6097b29b69f08a76c2b3af4212a0",
          "title": "Persisted Documents",
          "updated_at": "2026-04-07T07:45:51Z",
          "url": "https://api.github.com/repos/graphql-hive/router/pulls/868",
          "user": {
            "avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
            "events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
            "followers_url": "https://api.github.com/users/kamilkisiela/followers",
            "following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
            "gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
            "gravatar_id": "",
            "html_url": "https://github.com/kamilkisiela",
            "id": 8167190,
            "login": "kamilkisiela",
            "node_id": "MDQ6VXNlcjgxNjcxOTA=",
            "organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
            "received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
            "repos_url": "https://api.github.com/users/kamilkisiela/repos",
            "site_admin": false,
            "starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
            "subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
            "type": "User",
            "url": "https://api.github.com/users/kamilkisiela",
            "user_view_type": "public"
          }
        },
        "repository": {
          "allow_forking": true,
          "archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
          "archived": false,
          "assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
          "blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
          "branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
          "clone_url": "https://github.com/graphql-hive/router.git",
          "collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
          "comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
          "commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
          "compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
          "contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
          "contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
          "created_at": "2024-11-20T16:16:12Z",
          "custom_properties": {
            "vanta_production_branch_name": "main"
          },
          "default_branch": "main",
          "deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
          "description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
          "disabled": false,
          "downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
          "events_url": "https://api.github.com/repos/graphql-hive/router/events",
          "fork": false,
          "forks": 9,
          "forks_count": 9,
          "forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
          "full_name": "graphql-hive/router",
          "git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
          "git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
          "git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
          "git_url": "git://github.com/graphql-hive/router.git",
          "has_discussions": false,
          "has_downloads": true,
          "has_issues": true,
          "has_pages": false,
          "has_projects": false,
          "has_pull_requests": true,
          "has_wiki": false,
          "homepage": "https://the-guild.dev/graphql/hive/docs/router",
          "hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
          "html_url": "https://github.com/graphql-hive/router",
          "id": 891604244,
          "is_template": false,
          "issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
          "issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
          "issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
          "keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
          "labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
          "language": "Rust",
          "languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
          "license": {
            "key": "mit",
            "name": "MIT License",
            "node_id": "MDc6TGljZW5zZTEz",
            "spdx_id": "MIT",
            "url": "https://api.github.com/licenses/mit"
          },
          "merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
          "milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
          "mirror_url": null,
          "name": "router",
          "node_id": "R_kgDONSTNFA",
          "notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
          "open_issues": 65,
          "open_issues_count": 65,
          "owner": {
            "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
            "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
            "followers_url": "https://api.github.com/users/graphql-hive/followers",
            "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
            "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
            "gravatar_id": "",
            "html_url": "https://github.com/graphql-hive",
            "id": 182742256,
            "login": "graphql-hive",
            "node_id": "O_kgDOCuRs8A",
            "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
            "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
            "repos_url": "https://api.github.com/users/graphql-hive/repos",
            "site_admin": false,
            "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
            "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
            "type": "Organization",
            "url": "https://api.github.com/users/graphql-hive",
            "user_view_type": "public"
          },
          "private": false,
          "pull_request_creation_policy": "all",
          "pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
          "pushed_at": "2026-04-07T07:45:49Z",
          "releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
          "size": 6191,
          "ssh_url": "git@github.com:graphql-hive/router.git",
          "stargazers_count": 81,
          "stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
          "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
          "subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
          "subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
          "svn_url": "https://github.com/graphql-hive/router",
          "tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
          "teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
          "topics": [
            "apollo-federation",
            "federation",
            "federation-gateway",
            "graphql",
            "graphql-federation",
            "router"
          ],
          "trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
          "updated_at": "2026-04-03T14:14:41Z",
          "url": "https://api.github.com/repos/graphql-hive/router",
          "visibility": "public",
          "watchers": 81,
          "watchers_count": 81,
          "web_commit_signoff_required": false
        },
        "sender": {
          "avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
          "events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
          "followers_url": "https://api.github.com/users/kamilkisiela/followers",
          "following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
          "gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
          "gravatar_id": "",
          "html_url": "https://github.com/kamilkisiela",
          "id": 8167190,
          "login": "kamilkisiela",
          "node_id": "MDQ6VXNlcjgxNjcxOTA=",
          "organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
          "received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
          "repos_url": "https://api.github.com/users/kamilkisiela/repos",
          "site_admin": false,
          "starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
          "type": "User",
          "url": "https://api.github.com/users/kamilkisiela",
          "user_view_type": "public"
        }
      },
      "github_job": "docker",
      "github_ref": "refs/pull/868/merge",
      "github_ref_name": "868/merge",
      "github_ref_protected": "false",
      "github_ref_type": "branch",
      "github_repository": "graphql-hive/router",
      "github_repository_id": "891604244",
      "github_repository_owner": "graphql-hive",
      "github_repository_owner_id": "182742256",
      "github_run_attempt": "1",
      "github_run_id": "24070370021",
      "github_run_number": "2208",
      "github_runner_arch": "X64",
      "github_runner_environment": "github-hosted",
      "github_runner_image_os": "ubuntu24",
      "github_runner_image_version": "20260329.72.1",
      "github_runner_name": "GitHub Actions 1000721434",
      "github_runner_os": "Linux",
      "github_runner_tracking_id": "github_8655defb-26eb-40c2-8a21-162a9c1e97e6",
      "github_server_url": "https://github.com",
      "github_triggering_actor": "kamilkisiela",
      "github_workflow": "build-router",
      "github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/868/merge",
      "github_workflow_sha": "1ac238648612eef6ee22abdcede3f39ad64ac0e1",
      "platform": "linux/amd64"
    }
  }
},
"buildx.build.provenance/linux/arm64": {
  "builder": {
    "id": "https://github.com/graphql-hive/router/actions/runs/24070370021/attempts/1"
  },
  "buildType": "https://mobyproject.org/buildkit@v1",
  "materials": [
    {
      "uri": "pkg:docker/docker/dockerfile@1.22",
      "digest": {
        "sha256": "4a43a54dd1fedceb30ba47e76cfcf2b47304f4161c0caeac2db1c61804ea3c91"
      }
    },
    {
      "uri": "pkg:docker/gcr.io/distroless/cc-debian12@latest?platform=linux%2Farm64",
      "digest": {
        "sha256": "329e54034ce498f9c6b345044e8f530c6691f99e94a92446f68c0adf9baa8464"
      }
    }
  ],
  "invocation": {
    "configSource": {
      "entryPoint": "router.Dockerfile"
    },
    "parameters": {
      "frontend": "gateway.v0",
      "args": {
        "cmdline": "docker/dockerfile:1.22",
        "label:org.opencontainers.image.created": "2026-04-07T08:00:43.205Z",
        "label:org.opencontainers.image.description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
        "label:org.opencontainers.image.licenses": "MIT",
        "label:org.opencontainers.image.revision": "1ac238648612eef6ee22abdcede3f39ad64ac0e1",
        "label:org.opencontainers.image.source": "https://github.com/graphql-hive/router",
        "label:org.opencontainers.image.title": "router",
        "label:org.opencontainers.image.url": "https://github.com/graphql-hive/router",
        "label:org.opencontainers.image.vendor": "theguild",
        "label:org.opencontainers.image.version": "pr-868",
        "source": "docker/dockerfile:1.22"
      },
      "locals": [
        {
          "name": "context"
        },
        {
          "name": "dockerfile"
        }
      ]
    },
    "environment": {
      "github_actor": "kamilkisiela",
      "github_actor_id": "8167190",
      "github_event_name": "pull_request",
      "github_event_payload": {
        "action": "synchronize",
        "after": "c47dac4cfd7e6097b29b69f08a76c2b3af4212a0",
        "before": "38a2c117ba0c8b2473fff17e19b2ff4d68ddc41e",
        "enterprise": {
          "avatar_url": "https://avatars.githubusercontent.com/b/187753?v=4",
          "created_at": "2024-07-02T08:52:28Z",
          "description": "",
          "html_url": "https://github.com/enterprises/the-guild",
          "id": 187753,
          "name": "The Guild",
          "node_id": "E_kgDOAALdaQ",
          "slug": "the-guild",
          "updated_at": "2026-03-11T16:47:15Z",
          "website_url": "https://the-guild.dev/"
        },
        "number": 868,
        "organization": {
          "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
          "description": "Schema registry, analytics and gateway for GraphQL federation and other GraphQL APIs.",
          "events_url": "https://api.github.com/orgs/graphql-hive/events",
          "hooks_url": "https://api.github.com/orgs/graphql-hive/hooks",
          "id": 182742256,
          "issues_url": "https://api.github.com/orgs/graphql-hive/issues",
          "login": "graphql-hive",
          "members_url": "https://api.github.com/orgs/graphql-hive/members{/member}",
          "node_id": "O_kgDOCuRs8A",
          "public_members_url": "https://api.github.com/orgs/graphql-hive/public_members{/member}",
          "repos_url": "https://api.github.com/orgs/graphql-hive/repos",
          "url": "https://api.github.com/orgs/graphql-hive"
        },
        "pull_request": {
          "_links": {
            "comments": {
              "href": "https://api.github.com/repos/graphql-hive/router/issues/868/comments"
            },
            "commits": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/868/commits"
            },
            "html": {
              "href": "https://github.com/graphql-hive/router/pull/868"
            },
            "issue": {
              "href": "https://api.github.com/repos/graphql-hive/router/issues/868"
            },
            "review_comment": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}"
            },
            "review_comments": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/868/comments"
            },
            "self": {
              "href": "https://api.github.com/repos/graphql-hive/router/pulls/868"
            },
            "statuses": {
              "href": "https://api.github.com/repos/graphql-hive/router/statuses/c47dac4cfd7e6097b29b69f08a76c2b3af4212a0"
            }
          },
          "active_lock_reason": null,
          "additions": 5966,
          "assignee": null,
          "assignees": [],
          "author_association": "CONTRIBUTOR",
          "auto_merge": null,
          "base": {
            "label": "graphql-hive:main",
            "ref": "main",
            "repo": {
              "allow_auto_merge": false,
              "allow_forking": true,
              "allow_merge_commit": false,
              "allow_rebase_merge": false,
              "allow_squash_merge": true,
              "allow_update_branch": true,
              "archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
              "archived": false,
              "assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
              "blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
              "branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
              "clone_url": "https://github.com/graphql-hive/router.git",
              "collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
              "comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
              "commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
              "compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
              "contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
              "contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
              "created_at": "2024-11-20T16:16:12Z",
              "default_branch": "main",
              "delete_branch_on_merge": true,
              "deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
              "description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
              "disabled": false,
              "downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
              "events_url": "https://api.github.com/repos/graphql-hive/router/events",
              "fork": false,
              "forks": 9,
              "forks_count": 9,
              "forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
              "full_name": "graphql-hive/router",
              "git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
              "git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
              "git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
              "git_url": "git://github.com/graphql-hive/router.git",
              "has_discussions": false,
              "has_downloads": true,
              "has_issues": true,
              "has_pages": false,
              "has_projects": false,
              "has_pull_requests": true,
              "has_wiki": false,
              "homepage": "https://the-guild.dev/graphql/hive/docs/router",
              "hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
              "html_url": "https://github.com/graphql-hive/router",
              "id": 891604244,
              "is_template": false,
              "issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
              "issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
              "issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
              "keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
              "labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
              "language": "Rust",
              "languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
              "license": {
                "key": "mit",
                "name": "MIT License",
                "node_id": "MDc6TGljZW5zZTEz",
                "spdx_id": "MIT",
                "url": "https://api.github.com/licenses/mit"
              },
              "merge_commit_message": "PR_TITLE",
              "merge_commit_title": "MERGE_MESSAGE",
              "merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
              "milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
              "mirror_url": null,
              "name": "router",
              "node_id": "R_kgDONSTNFA",
              "notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
              "open_issues": 65,
              "open_issues_count": 65,
              "owner": {
                "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
                "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
                "followers_url": "https://api.github.com/users/graphql-hive/followers",
                "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
                "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/graphql-hive",
                "id": 182742256,
                "login": "graphql-hive",
                "node_id": "O_kgDOCuRs8A",
                "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
                "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
                "repos_url": "https://api.github.com/users/graphql-hive/repos",
                "site_admin": false,
                "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/graphql-hive",
                "user_view_type": "public"
              },
              "private": false,
              "pull_request_creation_policy": "all",
              "pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
              "pushed_at": "2026-04-07T07:45:49Z",
              "releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
              "size": 6191,
              "squash_merge_commit_message": "PR_BODY",
              "squash_merge_commit_title": "PR_TITLE",
              "ssh_url": "git@github.com:graphql-hive/router.git",
              "stargazers_count": 81,
              "stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
              "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
              "subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
              "subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
              "svn_url": "https://github.com/graphql-hive/router",
              "tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
              "teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
              "topics": [
                "apollo-federation",
                "federation",
                "federation-gateway",
                "graphql",
                "graphql-federation",
                "router"
              ],
              "trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
              "updated_at": "2026-04-03T14:14:41Z",
              "url": "https://api.github.com/repos/graphql-hive/router",
              "use_squash_pr_title_as_default": true,
              "visibility": "public",
              "watchers": 81,
              "watchers_count": 81,
              "web_commit_signoff_required": false
            },
            "sha": "03b6d920b8d6669f1977401e3c47f6ffca2ad520",
            "user": {
              "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
              "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
              "followers_url": "https://api.github.com/users/graphql-hive/followers",
              "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
              "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
              "gravatar_id": "",
              "html_url": "https://github.com/graphql-hive",
              "id": 182742256,
              "login": "graphql-hive",
              "node_id": "O_kgDOCuRs8A",
              "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
              "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
              "repos_url": "https://api.github.com/users/graphql-hive/repos",
              "site_admin": false,
              "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
              "type": "Organization",
              "url": "https://api.github.com/users/graphql-hive",
              "user_view_type": "public"
            }
          },
          "body": "This PR introduces Persisted Documents support with configurable extraction and storage, plus lot of e2e tests.\r\n\r\nCloses #311\r\n\r\n---\r\n\r\nDocumentation PR: https://github.com/graphql-hive/docs/pull/76\r\n\r\n - Preview of [security/persisted-documents](https://dc3c070a-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/security/persisted-documents)\r\n - Preview of [configuration/persisted_documents](https://dc3c070a-hive-platform-docs.theguild.workers.dev/graphql/hive/docs/router/configuration/persisted_documents)\r\n\r\n---\r\n\r\nSupports document ID extraction from:\r\n  - `documentId` body field or URL query param (by default)\r\n  - Apollo-style `extensions.persistedQuery.sha256Hash` (by default)\r\n  - custom `json_path` (like `doc_id` or `extensions.whatever.id`\r\n  - `url_query_param` (like `?doc_id=123`)\r\n  - `url_path_param` (like `/graphql/:id`)\r\n \r\n\r\nIn the example below, we first look for the path pattern and then the query param.\r\n```yaml\r\npersisted_documents:\r\n  extractors:\r\n    - type: url_path_param\r\n       template: /:id # relative to configured endpoint\r\n    - type: url_query_param\r\n       name: id # `?id=123\r\n```\r\n\r\nSupports different document storages:\r\n  - file manifest (in Apollo and Key-Value Relay style formats)\r\n  - Hive CDN (via `hive-console-sdk`)\r\n  \r\nFile storage has **watch mode** by default (works well with `relay-compiler --watch`), so when a file changes (we debounce the events for 150ms) the document manifest is reloaded and served fresh.\r\n\r\nHive storage includes syntax validation of the provided document id. We make sure we don't send what `str.replace('~', '/')` produces to the Hive CDN without verification. If we do, people would see 404 with no info that doc id is incorrect.\r\n\r\nIncludes `require_id: boolean` to control whether to require requests with document id only or not.\r\n\r\nIncludes `log_missing_id_requests: bool` (false by default) that logs information about requests with no document id. Helpful if you migrate from regular to queryless requests.\r\n\r\nRegarding Hive CDN. We don't rely only on `appName~appVersion~documentId` format of the document id, but app's name and version can be inferred from client identification headers (`graphql-client-name` etc - configurable via telemetry settings). We support it for reasons mentioned in the Slack Canvas doc (better DX and reusable `clientAwarness` feature of Apollo Client).\r\n\r\nI also added two metrics to measure:\r\n- requests with no document id - so devs know that some requests still send no id\r\n- document resolution failures - so devs know that some requests with doc id that has no document text\r\n\r\n## Noteworthy implementation details\r\n\r\nPersisted documents are implemented under `pipeline/persisted_documents/*` with clear split:\r\n  - extraction (`extract/*`)\r\n  - resolution (`resolve/*`)\r\n  - runtime (`mod.rs`, `types.rs`)\r\n\r\nCloses #867 - as I introduced single-flight resolution of documents in the SDK. The **Err had to be cloanable** (otherwise I would have to change the API to return Arc<Err>), so some error enum variants in the SDK was converted to `String` instead of raw errors from 3rd-party libraries.\r\n\r\nI also added a **negative cache** to store non 2XX requests for 5s (configurable, but in SDK it's disabled by default) to not keep repeating the same requests that eventually give errors or 404s.\r\n\r\nI cleaned up and moved the code responsible for preparation of graphql params, decoding of GET and POST payloads into `GraphQLGetInput` and `GraphQLPostInput` and `OperationPreparation` structs. This way the flow is clear, like what happens when we receive GET request, what when we receive POST, and how it's all translated to what the rest of the pipeline expects. It's in `bin/router/src/pipeline/execution_request.rs`.\r\n\r\nI did bunch of tricks to make sure we're performant:\r\n- custom query param reader (based on `memchr`)\r\n- conditional extraction of non standard JSON fields (fields that are not `query`, `extensions` etc)\r\n- built-in extraction of `documentId` during deserialization\r\n- supafast validation of document ids (based on `memchr`)\r\n\r\n---\r\n\r\nThere are many new lines of code, but majority is just e2e tests.\r\n\r\nFor reviewers, I recommend to check:\r\n- `docs/persisted-documents` to understand what I built and why\r\n- `bin/router/src/pipeline/persisted_documents` - pretty much everything related to persisted documents, how things are extracted, how documents are resolved\r\n- `bin/router/src/pipeline/execution_request.rs` - to understand how we convert POST and GET request into data consumed by the rest of the pipeline and this is when extraction and resolution of persisted documents happen.\r\n\r\nPerformance is identical as before (check `persisted-documents` bench in CI).",
          "changed_files": 60,
          "closed_at": null,
          "comments": 3,
          "comments_url": "https://api.github.com/repos/graphql-hive/router/issues/868/comments",
          "commits": 1,
          "commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/868/commits",
          "created_at": "2026-03-20T15:23:35Z",
          "deletions": 212,
          "diff_url": "https://github.com/graphql-hive/router/pull/868.diff",
          "draft": false,
          "head": {
            "label": "graphql-hive:kamil-persisted-documents",
            "ref": "kamil-persisted-documents",
            "repo": {
              "allow_auto_merge": false,
              "allow_forking": true,
              "allow_merge_commit": false,
              "allow_rebase_merge": false,
              "allow_squash_merge": true,
              "allow_update_branch": true,
              "archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
              "archived": false,
              "assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
              "blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
              "branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
              "clone_url": "https://github.com/graphql-hive/router.git",
              "collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
              "comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
              "commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
              "compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
              "contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
              "contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
              "created_at": "2024-11-20T16:16:12Z",
              "default_branch": "main",
              "delete_branch_on_merge": true,
              "deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
              "description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
              "disabled": false,
              "downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
              "events_url": "https://api.github.com/repos/graphql-hive/router/events",
              "fork": false,
              "forks": 9,
              "forks_count": 9,
              "forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
              "full_name": "graphql-hive/router",
              "git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
              "git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
              "git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
              "git_url": "git://github.com/graphql-hive/router.git",
              "has_discussions": false,
              "has_downloads": true,
              "has_issues": true,
              "has_pages": false,
              "has_projects": false,
              "has_pull_requests": true,
              "has_wiki": false,
              "homepage": "https://the-guild.dev/graphql/hive/docs/router",
              "hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
              "html_url": "https://github.com/graphql-hive/router",
              "id": 891604244,
              "is_template": false,
              "issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
              "issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
              "issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
              "keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
              "labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
              "language": "Rust",
              "languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
              "license": {
                "key": "mit",
                "name": "MIT License",
                "node_id": "MDc6TGljZW5zZTEz",
                "spdx_id": "MIT",
                "url": "https://api.github.com/licenses/mit"
              },
              "merge_commit_message": "PR_TITLE",
              "merge_commit_title": "MERGE_MESSAGE",
              "merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
              "milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
              "mirror_url": null,
              "name": "router",
              "node_id": "R_kgDONSTNFA",
              "notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
              "open_issues": 65,
              "open_issues_count": 65,
              "owner": {
                "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
                "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
                "followers_url": "https://api.github.com/users/graphql-hive/followers",
                "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
                "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/graphql-hive",
                "id": 182742256,
                "login": "graphql-hive",
                "node_id": "O_kgDOCuRs8A",
                "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
                "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
                "repos_url": "https://api.github.com/users/graphql-hive/repos",
                "site_admin": false,
                "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/graphql-hive",
                "user_view_type": "public"
              },
              "private": false,
              "pull_request_creation_policy": "all",
              "pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
              "pushed_at": "2026-04-07T07:45:49Z",
              "releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
              "size": 6191,
              "squash_merge_commit_message": "PR_BODY",
              "squash_merge_commit_title": "PR_TITLE",
              "ssh_url": "git@github.com:graphql-hive/router.git",
              "stargazers_count": 81,
              "stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
              "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
              "subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
              "subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
              "svn_url": "https://github.com/graphql-hive/router",
              "tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
              "teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
              "topics": [
                "apollo-federation",
                "federation",
                "federation-gateway",
                "graphql",
                "graphql-federation",
                "router"
              ],
              "trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
              "updated_at": "2026-04-03T14:14:41Z",
              "url": "https://api.github.com/repos/graphql-hive/router",
              "use_squash_pr_title_as_default": true,
              "visibility": "public",
              "watchers": 81,
              "watchers_count": 81,
              "web_commit_signoff_required": false
            },
            "sha": "c47dac4cfd7e6097b29b69f08a76c2b3af4212a0",
            "user": {
              "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
              "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
              "followers_url": "https://api.github.com/users/graphql-hive/followers",
              "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
              "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
              "gravatar_id": "",
              "html_url": "https://github.com/graphql-hive",
              "id": 182742256,
              "login": "graphql-hive",
              "node_id": "O_kgDOCuRs8A",
              "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
              "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
              "repos_url": "https://api.github.com/users/graphql-hive/repos",
              "site_admin": false,
              "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
              "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
              "type": "Organization",
              "url": "https://api.github.com/users/graphql-hive",
              "user_view_type": "public"
            }
          },
          "html_url": "https://github.com/graphql-hive/router/pull/868",
          "id": 3425522123,
          "issue_url": "https://api.github.com/repos/graphql-hive/router/issues/868",
          "labels": [],
          "locked": false,
          "maintainer_can_modify": false,
          "merge_commit_sha": "ee6b5ad2d1d8f6b463c21d5ef1d1863e5d1ce55f",
          "mergeable": null,
          "mergeable_state": "unknown",
          "merged": false,
          "merged_at": null,
          "merged_by": null,
          "milestone": null,
          "node_id": "PR_kwDONSTNFM7MLVHL",
          "number": 868,
          "patch_url": "https://github.com/graphql-hive/router/pull/868.patch",
          "rebaseable": null,
          "requested_reviewers": [],
          "requested_teams": [],
          "review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
          "review_comments": 19,
          "review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/868/comments",
          "state": "open",
          "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/c47dac4cfd7e6097b29b69f08a76c2b3af4212a0",
          "title": "Persisted Documents",
          "updated_at": "2026-04-07T07:45:51Z",
          "url": "https://api.github.com/repos/graphql-hive/router/pulls/868",
          "user": {
            "avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
            "events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
            "followers_url": "https://api.github.com/users/kamilkisiela/followers",
            "following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
            "gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
            "gravatar_id": "",
            "html_url": "https://github.com/kamilkisiela",
            "id": 8167190,
            "login": "kamilkisiela",
            "node_id": "MDQ6VXNlcjgxNjcxOTA=",
            "organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
            "received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
            "repos_url": "https://api.github.com/users/kamilkisiela/repos",
            "site_admin": false,
            "starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
            "subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
            "type": "User",
            "url": "https://api.github.com/users/kamilkisiela",
            "user_view_type": "public"
          }
        },
        "repository": {
          "allow_forking": true,
          "archive_url": "https://api.github.com/repos/graphql-hive/router/{archive_format}{/ref}",
          "archived": false,
          "assignees_url": "https://api.github.com/repos/graphql-hive/router/assignees{/user}",
          "blobs_url": "https://api.github.com/repos/graphql-hive/router/git/blobs{/sha}",
          "branches_url": "https://api.github.com/repos/graphql-hive/router/branches{/branch}",
          "clone_url": "https://github.com/graphql-hive/router.git",
          "collaborators_url": "https://api.github.com/repos/graphql-hive/router/collaborators{/collaborator}",
          "comments_url": "https://api.github.com/repos/graphql-hive/router/comments{/number}",
          "commits_url": "https://api.github.com/repos/graphql-hive/router/commits{/sha}",
          "compare_url": "https://api.github.com/repos/graphql-hive/router/compare/{base}...{head}",
          "contents_url": "https://api.github.com/repos/graphql-hive/router/contents/{+path}",
          "contributors_url": "https://api.github.com/repos/graphql-hive/router/contributors",
          "created_at": "2024-11-20T16:16:12Z",
          "custom_properties": {
            "vanta_production_branch_name": "main"
          },
          "default_branch": "main",
          "deployments_url": "https://api.github.com/repos/graphql-hive/router/deployments",
          "description": "Open-source (MIT) GraphQL Federation Router. Built with Rust for maximum performance and robustness.",
          "disabled": false,
          "downloads_url": "https://api.github.com/repos/graphql-hive/router/downloads",
          "events_url": "https://api.github.com/repos/graphql-hive/router/events",
          "fork": false,
          "forks": 9,
          "forks_count": 9,
          "forks_url": "https://api.github.com/repos/graphql-hive/router/forks",
          "full_name": "graphql-hive/router",
          "git_commits_url": "https://api.github.com/repos/graphql-hive/router/git/commits{/sha}",
          "git_refs_url": "https://api.github.com/repos/graphql-hive/router/git/refs{/sha}",
          "git_tags_url": "https://api.github.com/repos/graphql-hive/router/git/tags{/sha}",
          "git_url": "git://github.com/graphql-hive/router.git",
          "has_discussions": false,
          "has_downloads": true,
          "has_issues": true,
          "has_pages": false,
          "has_projects": false,
          "has_pull_requests": true,
          "has_wiki": false,
          "homepage": "https://the-guild.dev/graphql/hive/docs/router",
          "hooks_url": "https://api.github.com/repos/graphql-hive/router/hooks",
          "html_url": "https://github.com/graphql-hive/router",
          "id": 891604244,
          "is_template": false,
          "issue_comment_url": "https://api.github.com/repos/graphql-hive/router/issues/comments{/number}",
          "issue_events_url": "https://api.github.com/repos/graphql-hive/router/issues/events{/number}",
          "issues_url": "https://api.github.com/repos/graphql-hive/router/issues{/number}",
          "keys_url": "https://api.github.com/repos/graphql-hive/router/keys{/key_id}",
          "labels_url": "https://api.github.com/repos/graphql-hive/router/labels{/name}",
          "language": "Rust",
          "languages_url": "https://api.github.com/repos/graphql-hive/router/languages",
          "license": {
            "key": "mit",
            "name": "MIT License",
            "node_id": "MDc6TGljZW5zZTEz",
            "spdx_id": "MIT",
            "url": "https://api.github.com/licenses/mit"
          },
          "merges_url": "https://api.github.com/repos/graphql-hive/router/merges",
          "milestones_url": "https://api.github.com/repos/graphql-hive/router/milestones{/number}",
          "mirror_url": null,
          "name": "router",
          "node_id": "R_kgDONSTNFA",
          "notifications_url": "https://api.github.com/repos/graphql-hive/router/notifications{?since,all,participating}",
          "open_issues": 65,
          "open_issues_count": 65,
          "owner": {
            "avatar_url": "https://avatars.githubusercontent.com/u/182742256?v=4",
            "events_url": "https://api.github.com/users/graphql-hive/events{/privacy}",
            "followers_url": "https://api.github.com/users/graphql-hive/followers",
            "following_url": "https://api.github.com/users/graphql-hive/following{/other_user}",
            "gists_url": "https://api.github.com/users/graphql-hive/gists{/gist_id}",
            "gravatar_id": "",
            "html_url": "https://github.com/graphql-hive",
            "id": 182742256,
            "login": "graphql-hive",
            "node_id": "O_kgDOCuRs8A",
            "organizations_url": "https://api.github.com/users/graphql-hive/orgs",
            "received_events_url": "https://api.github.com/users/graphql-hive/received_events",
            "repos_url": "https://api.github.com/users/graphql-hive/repos",
            "site_admin": false,
            "starred_url": "https://api.github.com/users/graphql-hive/starred{/owner}{/repo}",
            "subscriptions_url": "https://api.github.com/users/graphql-hive/subscriptions",
            "type": "Organization",
            "url": "https://api.github.com/users/graphql-hive",
            "user_view_type": "public"
          },
          "private": false,
          "pull_request_creation_policy": "all",
          "pulls_url": "https://api.github.com/repos/graphql-hive/router/pulls{/number}",
          "pushed_at": "2026-04-07T07:45:49Z",
          "releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
          "size": 6191,
          "ssh_url": "git@github.com:graphql-hive/router.git",
          "stargazers_count": 81,
          "stargazers_url": "https://api.github.com/repos/graphql-hive/router/stargazers",
          "statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/{sha}",
          "subscribers_url": "https://api.github.com/repos/graphql-hive/router/subscribers",
          "subscription_url": "https://api.github.com/repos/graphql-hive/router/subscription",
          "svn_url": "https://github.com/graphql-hive/router",
          "tags_url": "https://api.github.com/repos/graphql-hive/router/tags",
          "teams_url": "https://api.github.com/repos/graphql-hive/router/teams",
          "topics": [
            "apollo-federation",
            "federation",
            "federation-gateway",
            "graphql",
            "graphql-federation",
            "router"
          ],
          "trees_url": "https://api.github.com/repos/graphql-hive/router/git/trees{/sha}",
          "updated_at": "2026-04-03T14:14:41Z",
          "url": "https://api.github.com/repos/graphql-hive/router",
          "visibility": "public",
          "watchers": 81,
          "watchers_count": 81,
          "web_commit_signoff_required": false
        },
        "sender": {
          "avatar_url": "https://avatars.githubusercontent.com/u/8167190?v=4",
          "events_url": "https://api.github.com/users/kamilkisiela/events{/privacy}",
          "followers_url": "https://api.github.com/users/kamilkisiela/followers",
          "following_url": "https://api.github.com/users/kamilkisiela/following{/other_user}",
          "gists_url": "https://api.github.com/users/kamilkisiela/gists{/gist_id}",
          "gravatar_id": "",
          "html_url": "https://github.com/kamilkisiela",
          "id": 8167190,
          "login": "kamilkisiela",
          "node_id": "MDQ6VXNlcjgxNjcxOTA=",
          "organizations_url": "https://api.github.com/users/kamilkisiela/orgs",
          "received_events_url": "https://api.github.com/users/kamilkisiela/received_events",
          "repos_url": "https://api.github.com/users/kamilkisiela/repos",
          "site_admin": false,
          "starred_url": "https://api.github.com/users/kamilkisiela/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/kamilkisiela/subscriptions",
          "type": "User",
          "url": "https://api.github.com/users/kamilkisiela",
          "user_view_type": "public"
        }
      },
      "github_job": "docker",
      "github_ref": "refs/pull/868/merge",
      "github_ref_name": "868/merge",
      "github_ref_protected": "false",
      "github_ref_type": "branch",
      "github_repository": "graphql-hive/router",
      "github_repository_id": "891604244",
      "github_repository_owner": "graphql-hive",
      "github_repository_owner_id": "182742256",
      "github_run_attempt": "1",
      "github_run_id": "24070370021",
      "github_run_number": "2208",
      "github_runner_arch": "X64",
      "github_runner_environment": "github-hosted",
      "github_runner_image_os": "ubuntu24",
      "github_runner_image_version": "20260329.72.1",
      "github_runner_name": "GitHub Actions 1000721434",
      "github_runner_os": "Linux",
      "github_runner_tracking_id": "github_8655defb-26eb-40c2-8a21-162a9c1e97e6",
      "github_server_url": "https://github.com",
      "github_triggering_actor": "kamilkisiela",
      "github_workflow": "build-router",
      "github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/868/merge",
      "github_workflow_sha": "1ac238648612eef6ee22abdcede3f39ad64ac0e1",
      "platform": "linux/amd64"
    }
  }
},
"buildx.build.ref": "builder-5c756281-6e9c-4a9e-b72b-bbd32f8ae975/builder-5c756281-6e9c-4a9e-b72b-bbd32f8ae9750/1n9uv3w3gf657a750ufs0gxu6",
"containerimage.descriptor": {
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "digest": "sha256:5bdd0c688a22724b9caac87f84fb7e76621ce6c981e76bd0da06f7cadc581286",
  "size": 1609
},
"containerimage.digest": "sha256:5bdd0c688a22724b9caac87f84fb7e76621ce6c981e76bd0da06f7cadc581286",
"image.name": "ghcr.io/graphql-hive/router:pr-868,ghcr.io/graphql-hive/router:sha-1ac2386"
}

@enisdenjo enisdenjo force-pushed the main branch 3 times, most recently from 511f910 to e09e78a Compare April 1, 2026 19:17
@kamilkisiela kamilkisiela force-pushed the kamil-persisted-documents branch 3 times, most recently from 14c8ae8 to 908e8e6 Compare April 2, 2026 16:30
@kamilkisiela kamilkisiela marked this pull request as ready for review April 2, 2026 16:32
@kamilkisiela kamilkisiela force-pushed the kamil-persisted-documents branch from c4041bb to 99e3d34 Compare April 2, 2026 16:46
@kamilkisiela
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for persisted documents in the Hive Router, allowing for configurable extraction and storage backends. The implementation includes a new PersistedDocumentsRuntime for managing document resolution, support for multiple extraction methods (JSON path, URL path parameters, and query parameters), and integration with file-based and Hive CDN storage. I have reviewed the changes and provided feedback on improving the file watcher implementation and ensuring that file I/O operations are handled asynchronously to avoid blocking the executor.

@kamilkisiela kamilkisiela force-pushed the kamil-persisted-documents branch from cf3c9bd to e289424 Compare April 2, 2026 19:00
@kamilkisiela kamilkisiela force-pushed the kamil-persisted-documents branch from 38a2c11 to c47dac4 Compare April 7, 2026 07:45
#[serde(deny_unknown_fields)]
pub struct PersistedDocumentsHiveCircuitBreakerConfig {
#[serde(default = "default_circuit_breaker_error_threshold")]
pub error_threshold: f32,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we use Percentage type we have here instead of f32?

Unknown,
}

impl From<reqwest_middleware::Error> for PersistedDocumentsError {
Copy link
Copy Markdown
Member

@ardatan ardatan Apr 9, 2026

Choose a reason for hiding this comment

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

Are these needed for cloning?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PersistedDocumentsManager: replace get+insert miss handling with single-flight loading (moka try_get_with_by_ref) RFC: Persisted Documents

2 participants