[SUPERSEDED] feat(router,executor): allow plugins to override the supergraph from on_http_request - #1269
[SUPERSEDED] feat(router,executor): allow plugins to override the supergraph from on_http_request#1269enisdenjo wants to merge 14 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the ability for plugins to override the schema state for a request during the on_http_request hook, allowing schema overrides to persist throughout the entire execution pipeline, including introspection. To support this, public constructors from_supergraph_sdl and from_supergraph_document have been added to SchemaState, and a new replace_schema plugin example has been provided. The review feedback suggests exposing SupergraphManagerError in the public API and using it in the example plugin instead of Box<dyn std::error::Error>. Additionally, it recommends documenting that set_schema_state specifically requires hive_router::SchemaState to prevent silent fallback, and optimizing the AST stripping logic in the example plugin to avoid inefficient structural equality checks on entire definitions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR adds a new capability for plugins to override the entire SchemaState during the early on_http_request hook, ensuring that the selected schema variant is consistently used across the full GraphQL pipeline (parse/validate/normalize/plan/execute) and also for introspection.
Changes:
- Add
OnHttpRequestHookPayload::set_schema_stateso plugins can provide a per-requestArc<SchemaState>early in the request lifecycle. - Update the router’s HTTP and websocket entrypoints to honor a plugin-provided
SchemaStatefrom request extensions. - Introduce a new
replace_schemaplugin example + e2e test demonstrating validation + introspection behavior with schema variants.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
lib/executor/src/plugins/hooks/on_http_request.rs |
Adds set_schema_state API for early per-request schema override. |
bin/router/src/lib.rs |
Ensures GraphQL HTTP handler uses overridden SchemaState from request extensions. |
bin/router/src/pipeline/websocket_server.rs |
Ensures websocket handler uses overridden SchemaState from request extensions. |
bin/router/src/schema_state.rs |
Exposes new public constructors for plugin-owned SchemaState instances. |
plugin_examples/replace_schema/* |
Adds a complete example plugin, schema SDL, config, and an e2e test proving behavior (validation + introspection). |
plugin_examples/Cargo.toml |
Adds replace_schema to the plugin examples workspace. |
plugin_examples/Cargo.lock |
Updates lockfile to include the new example and dependency resolution. |
.changeset/replace_the_schema_state_in_the_on_http_request_plugin_hook.md |
Documents the new feature and plugin author caveats for release notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
🐋 This PR was built and pushed to the following Docker images: Image Names: Platforms: Image Tags: Docker metadata{
"buildx.build.provenance/linux/amd64": {
"builder": {
"id": "https://github.com/graphql-hive/router/actions/runs/29406540482/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": "7ee09f36862efbdbf70422db263e411c2618409ca46faa555bd5b636155307df"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "router.Dockerfile"
},
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1.22",
"label:org.opencontainers.image.created": "2026-07-15T10:13:45.406Z",
"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": "05c61de1c1c019a12095dfd437e446628711e70c",
"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-1269",
"source": "docker/dockerfile:1.22"
},
"locals": [
{
"name": "context"
},
{
"name": "dockerfile"
}
],
"root": {
"configSource": {
"path": "router.Dockerfile"
},
"request": {
"args": {
"label:org.opencontainers.image.created": "2026-07-15T10:13:45.406Z",
"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": "05c61de1c1c019a12095dfd437e446628711e70c",
"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-1269",
"vcs:localdir:context": ".",
"vcs:localdir:dockerfile": "docker",
"vcs:revision": "05c61de1c1c019a12095dfd437e446628711e70c",
"vcs:source": "https://github.com/graphql-hive/router"
}
}
},
"compatibilityVersion": 20
},
"environment": {
"github_actor": "enisdenjo",
"github_actor_id": "11807600",
"github_event_name": "pull_request",
"github_event_payload": {
"action": "synchronize",
"after": "75641294d76f286ea7b1d8f00009adb8603a22ed",
"before": "b265bdab8e07eb32505402e88cf4be3473bcd583",
"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-07-11T07:16:45Z",
"website_url": "https://the-guild.dev/"
},
"number": 1269,
"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/1269/comments"
},
"commits": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1269/commits"
},
"html": {
"href": "https://github.com/graphql-hive/router/pull/1269"
},
"issue": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/1269"
},
"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/1269/comments"
},
"self": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1269"
},
"statuses": {
"href": "https://api.github.com/repos/graphql-hive/router/statuses/75641294d76f286ea7b1d8f00009adb8603a22ed"
}
},
"active_lock_reason": null,
"additions": 1739,
"assignee": null,
"assignees": [],
"author_association": "MEMBER",
"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": 16,
"forks_count": 16,
"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": 60,
"open_issues_count": 60,
"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-07-15T09:59:43Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10342,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 94,
"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-07-15T09:09:50Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 94,
"watchers_count": 94,
"web_commit_signoff_required": false
},
"sha": "a9da97a18e4fe8ace3520fcee6d225dc93a31290",
"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": "Closes #1137\r\n\r\nAllow a plugin to select a supergraph document during `on_http_request` and have it hold for the entire request pipeline, including validation, normalization, planning, execution, and introspection. Previously, plugins could only replace the validation schema through `on_graphql_validation`, so fields hidden from validation could still appear through `__schema` and `__type` introspection.\r\n\r\nThe new `on_http_request` hook API accepts a schema document:\r\n\r\n```rust\r\nfn on_http_request<'req>(\r\n &'req self,\r\n payload: OnHttpRequestHookPayload<'req>,\r\n) -> OnHttpRequestHookResult<'req> {\r\n if let Some(document) = self.schema_document_for_request(&payload) {\r\n payload.set_schema_document(document.clone());\r\n }\r\n payload.proceed()\r\n}\r\n```\r\n\r\nThe plugin owns stable `Arc<Document>` schema variants. After all `on_http_request` hooks run, the router resolves the selected document into an internal `Arc<SchemaState>` and stores that state in request extensions. The existing HTTP and WebSocket entry points then use that request-specific state for the full operation lifecycle.\r\n\r\n### Why resolve a whole `SchemaState` internally?\r\n\r\nThe request pipeline consumes both:\r\n\r\n- the current `SupergraphData`\r\n- schema-coupled state, including the plan, validation, normalization, and demand-control formula caches\r\n\r\nThese caches are not all keyed by the schema checksum. Replacing only `SupergraphData` could therefore allow schema B to reuse normalized operations, query plans, or demand-control formulas produced under schema A.\r\n\r\nEach selected document is instead resolved to its own `SchemaState`, which contains its own `SupergraphData`, query planner, caches, demand-control runtime, and callback subscriptions map. This keeps schema-derived state isolated while letting repeated requests reuse the expensive planner build. The request-deduplication fingerprint already includes `schema_checksum`, so request deduplication also remains isolated across schema variants.\r\n\r\nKeeping `SchemaState` internal also avoids exposing router configuration and telemetry internals to plugins. The router builds each state with its actual `HiveRouterConfig` and `TelemetryContext`.\r\n\r\n### Schema-state cache\r\n\r\nThe router keeps up to 10 plugin-selected schema states in a fixed-size cache:\r\n\r\n- Documents are keyed by `Arc` allocation identity, using `Arc::ptr_eq`.\r\n- The first request for a document builds its `SchemaState` synchronously.\r\n- Later requests using the same `Arc<Document>` reuse the existing state.\r\n- The cache uses strict FIFO eviction. When an 11ht document is inserted, the oldest inserted document is evicted.\r\n- Cache hits do not refresh the eviction order. (is this ok?)\r\n- Eviction only removes the cache's reference.<br/>\r\n Meaning, in-flight requests and subscriptions holding the evicted `Arc<SchemaState>` continue safely.\r\n- Selecting an evicted document again rebuilds its state.\r\n- A failed build is not cached and does not evict a valid entry.\r\n- A failed build returns HTTP 500 instead of falling back to the default schema, because fallback could expose fields the plugin intended to remove or change.\r\n\r\nMiss construction is serialized so concurrent first requests cannot build the same cached state more than once.\r\n\r\n### Drop `with_schema` from the `on_graphql_validation` plugin hook\r\n\r\nRemove `OnGraphQLValidationStartHookPayload::with_schema`. Method was broken by design, it replaced the schema only for validation while parsing, introspection, normalization, planning, and execution continued using the request's original schema state. This could make a field disappear during validation while remaining visible through introspection, and it could leave schema-derived caches and planning state inconsistent with the schema used to validate the operation.\r\n\r\nPlugins that need a request-specific schema should now build and retain a stable `Arc<Document>` for each schema variant and select it in `on_http_request`:\r\n\r\n```rust\r\nfn on_http_request<'req>(\r\n &'req self,\r\n payload: OnHttpRequestHookPayload<'req>,\r\n) -> OnHttpRequestHookResult<'req> {\r\n payload.set_schema_document(self.document_for_request(&payload).clone());\r\n payload.proceed()\r\n}\r\n```\r\n\r\n### Stable document responsibility\r\n\r\nPlugins must construct each schema variant once and retain the same `Arc<Document>` in their own state. Parsing a document or allocating a new `Arc<Document>` per request defeats cache reuse and triggers an expensive planner build for every request.\r\n\r\nFor example:\r\n\r\n```rust\r\npub struct ReplaceSchemaPlugin {\r\n variants: HashMap<&'static str, Arc<Document>>,\r\n}\r\n\r\nfn on_http_request<'req>(\r\n &'req self,\r\n payload: OnHttpRequestHookPayload<'req>,\r\n) -> OnHttpRequestHookResult<'req> {\r\n if let Some(document) = self.variants.get(\"basic\") {\r\n payload.set_schema_document(document.clone());\r\n }\r\n\r\n payload.proceed()\r\n}\r\n```\r\n\r\nSee [`plugin_examples/replace_schema`](https://github.com/graphql-hive/router/tree/main/plugin_examples/replace_schema) for a complete example that builds stable feature-specific documents and selects one from a request header.\r\n\r\n### Caveats for plugin authors\r\n\r\n- Building a `SchemaState` includes a full query planner build. The first request for each document therefore has additional latency.\r\n- Router-side supergraph reloads do not mutate or invalidate cached plugin-selected states and do not force-close subscriptions using them.\r\n- `on_http_request` is synchronous. External feature or project lookups must be refreshed asynchronously in the plugin lifecycle so the hook only performs a synchronous lookup from request data to a stable `Arc<Document>`.\r\n\r\n### TODO\r\n\r\n- [ ] plugin-selected states do not register callback heartbeat enforcer background task\r\n- [ ] document schema state cache has a max cap of 10, is this ok? should it be configurable? should the plugin author be able to manually evict?\r\n- [ ] docs\r\n",
"changed_files": 19,
"closed_at": null,
"comments": 3,
"comments_url": "https://api.github.com/repos/graphql-hive/router/issues/1269/comments",
"commits": 14,
"commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/1269/commits",
"created_at": "2026-07-09T15:33:43Z",
"deletions": 396,
"diff_url": "https://github.com/graphql-hive/router/pull/1269.diff",
"draft": false,
"head": {
"label": "graphql-hive:super-replace",
"ref": "super-replace",
"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": 16,
"forks_count": 16,
"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": 60,
"open_issues_count": 60,
"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-07-15T09:59:43Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10342,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 94,
"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-07-15T09:09:50Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 94,
"watchers_count": 94,
"web_commit_signoff_required": false
},
"sha": "75641294d76f286ea7b1d8f00009adb8603a22ed",
"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/1269",
"id": 4023143227,
"issue_url": "https://api.github.com/repos/graphql-hive/router/issues/1269",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "d95402bc12238b622fc3d7dbfac56f34328772ef",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "PR_kwDONSTNFM7vzEs7",
"number": 1269,
"patch_url": "https://github.com/graphql-hive/router/pull/1269.patch",
"rebaseable": null,
"requested_reviewers": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/3680083?v=4",
"events_url": "https://api.github.com/users/dotansimha/events{/privacy}",
"followers_url": "https://api.github.com/users/dotansimha/followers",
"following_url": "https://api.github.com/users/dotansimha/following{/other_user}",
"gists_url": "https://api.github.com/users/dotansimha/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/dotansimha",
"id": 3680083,
"login": "dotansimha",
"node_id": "MDQ6VXNlcjM2ODAwODM=",
"organizations_url": "https://api.github.com/users/dotansimha/orgs",
"received_events_url": "https://api.github.com/users/dotansimha/received_events",
"repos_url": "https://api.github.com/users/dotansimha/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/dotansimha/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dotansimha/subscriptions",
"type": "User",
"url": "https://api.github.com/users/dotansimha",
"user_view_type": "public"
},
{
"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"
}
],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
"review_comments": 9,
"review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/1269/comments",
"state": "open",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/75641294d76f286ea7b1d8f00009adb8603a22ed",
"title": "feat(router,executor): allow plugins to override the supergraph from on_http_request",
"updated_at": "2026-07-15T09:59:45Z",
"url": "https://api.github.com/repos/graphql-hive/router/pulls/1269",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/11807600?v=4",
"events_url": "https://api.github.com/users/enisdenjo/events{/privacy}",
"followers_url": "https://api.github.com/users/enisdenjo/followers",
"following_url": "https://api.github.com/users/enisdenjo/following{/other_user}",
"gists_url": "https://api.github.com/users/enisdenjo/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/enisdenjo",
"id": 11807600,
"login": "enisdenjo",
"node_id": "MDQ6VXNlcjExODA3NjAw",
"organizations_url": "https://api.github.com/users/enisdenjo/orgs",
"received_events_url": "https://api.github.com/users/enisdenjo/received_events",
"repos_url": "https://api.github.com/users/enisdenjo/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/enisdenjo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/enisdenjo/subscriptions",
"type": "User",
"url": "https://api.github.com/users/enisdenjo",
"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": 16,
"forks_count": 16,
"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": 60,
"open_issues_count": 60,
"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-07-15T09:59:43Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10342,
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 94,
"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-07-15T09:09:50Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"visibility": "public",
"watchers": 94,
"watchers_count": 94,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/11807600?v=4",
"events_url": "https://api.github.com/users/enisdenjo/events{/privacy}",
"followers_url": "https://api.github.com/users/enisdenjo/followers",
"following_url": "https://api.github.com/users/enisdenjo/following{/other_user}",
"gists_url": "https://api.github.com/users/enisdenjo/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/enisdenjo",
"id": 11807600,
"login": "enisdenjo",
"node_id": "MDQ6VXNlcjExODA3NjAw",
"organizations_url": "https://api.github.com/users/enisdenjo/orgs",
"received_events_url": "https://api.github.com/users/enisdenjo/received_events",
"repos_url": "https://api.github.com/users/enisdenjo/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/enisdenjo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/enisdenjo/subscriptions",
"type": "User",
"url": "https://api.github.com/users/enisdenjo",
"user_view_type": "public"
}
},
"github_job": "docker",
"github_ref": "refs/pull/1269/merge",
"github_ref_name": "1269/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": "29406540482",
"github_run_number": "4403",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20260705.232.1",
"github_runner_name": "GitHub Actions 1000911136",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_30c2069b-a5b1-481e-81ed-308c60248a9d",
"github_server_url": "https://github.com",
"github_triggering_actor": "enisdenjo",
"github_workflow": "build-router",
"github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/1269/merge",
"github_workflow_sha": "05c61de1c1c019a12095dfd437e446628711e70c",
"platform": "linux/amd64"
}
}
},
"buildx.build.provenance/linux/arm64": {
"builder": {
"id": "https://github.com/graphql-hive/router/actions/runs/29406540482/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": "7ee09f36862efbdbf70422db263e411c2618409ca46faa555bd5b636155307df"
}
}
],
"invocation": {
"configSource": {
"entryPoint": "router.Dockerfile"
},
"parameters": {
"frontend": "gateway.v0",
"args": {
"cmdline": "docker/dockerfile:1.22",
"label:org.opencontainers.image.created": "2026-07-15T10:13:45.406Z",
"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": "05c61de1c1c019a12095dfd437e446628711e70c",
"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-1269",
"source": "docker/dockerfile:1.22"
},
"locals": [
{
"name": "context"
},
{
"name": "dockerfile"
}
],
"root": {
"configSource": {
"path": "router.Dockerfile"
},
"request": {
"args": {
"label:org.opencontainers.image.created": "2026-07-15T10:13:45.406Z",
"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": "05c61de1c1c019a12095dfd437e446628711e70c",
"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-1269",
"vcs:localdir:context": ".",
"vcs:localdir:dockerfile": "docker",
"vcs:revision": "05c61de1c1c019a12095dfd437e446628711e70c",
"vcs:source": "https://github.com/graphql-hive/router"
}
}
},
"compatibilityVersion": 20
},
"environment": {
"github_actor": "enisdenjo",
"github_actor_id": "11807600",
"github_event_name": "pull_request",
"github_event_payload": {
"action": "synchronize",
"after": "75641294d76f286ea7b1d8f00009adb8603a22ed",
"before": "b265bdab8e07eb32505402e88cf4be3473bcd583",
"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-07-11T07:16:45Z",
"website_url": "https://the-guild.dev/"
},
"number": 1269,
"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/1269/comments"
},
"commits": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1269/commits"
},
"html": {
"href": "https://github.com/graphql-hive/router/pull/1269"
},
"issue": {
"href": "https://api.github.com/repos/graphql-hive/router/issues/1269"
},
"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/1269/comments"
},
"self": {
"href": "https://api.github.com/repos/graphql-hive/router/pulls/1269"
},
"statuses": {
"href": "https://api.github.com/repos/graphql-hive/router/statuses/75641294d76f286ea7b1d8f00009adb8603a22ed"
}
},
"active_lock_reason": null,
"additions": 1739,
"assignee": null,
"assignees": [],
"author_association": "MEMBER",
"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": 16,
"forks_count": 16,
"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": 60,
"open_issues_count": 60,
"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-07-15T09:59:43Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10342,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 94,
"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-07-15T09:09:50Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 94,
"watchers_count": 94,
"web_commit_signoff_required": false
},
"sha": "a9da97a18e4fe8ace3520fcee6d225dc93a31290",
"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": "Closes #1137\r\n\r\nAllow a plugin to select a supergraph document during `on_http_request` and have it hold for the entire request pipeline, including validation, normalization, planning, execution, and introspection. Previously, plugins could only replace the validation schema through `on_graphql_validation`, so fields hidden from validation could still appear through `__schema` and `__type` introspection.\r\n\r\nThe new `on_http_request` hook API accepts a schema document:\r\n\r\n```rust\r\nfn on_http_request<'req>(\r\n &'req self,\r\n payload: OnHttpRequestHookPayload<'req>,\r\n) -> OnHttpRequestHookResult<'req> {\r\n if let Some(document) = self.schema_document_for_request(&payload) {\r\n payload.set_schema_document(document.clone());\r\n }\r\n payload.proceed()\r\n}\r\n```\r\n\r\nThe plugin owns stable `Arc<Document>` schema variants. After all `on_http_request` hooks run, the router resolves the selected document into an internal `Arc<SchemaState>` and stores that state in request extensions. The existing HTTP and WebSocket entry points then use that request-specific state for the full operation lifecycle.\r\n\r\n### Why resolve a whole `SchemaState` internally?\r\n\r\nThe request pipeline consumes both:\r\n\r\n- the current `SupergraphData`\r\n- schema-coupled state, including the plan, validation, normalization, and demand-control formula caches\r\n\r\nThese caches are not all keyed by the schema checksum. Replacing only `SupergraphData` could therefore allow schema B to reuse normalized operations, query plans, or demand-control formulas produced under schema A.\r\n\r\nEach selected document is instead resolved to its own `SchemaState`, which contains its own `SupergraphData`, query planner, caches, demand-control runtime, and callback subscriptions map. This keeps schema-derived state isolated while letting repeated requests reuse the expensive planner build. The request-deduplication fingerprint already includes `schema_checksum`, so request deduplication also remains isolated across schema variants.\r\n\r\nKeeping `SchemaState` internal also avoids exposing router configuration and telemetry internals to plugins. The router builds each state with its actual `HiveRouterConfig` and `TelemetryContext`.\r\n\r\n### Schema-state cache\r\n\r\nThe router keeps up to 10 plugin-selected schema states in a fixed-size cache:\r\n\r\n- Documents are keyed by `Arc` allocation identity, using `Arc::ptr_eq`.\r\n- The first request for a document builds its `SchemaState` synchronously.\r\n- Later requests using the same `Arc<Document>` reuse the existing state.\r\n- The cache uses strict FIFO eviction. When an 11ht document is inserted, the oldest inserted document is evicted.\r\n- Cache hits do not refresh the eviction order. (is this ok?)\r\n- Eviction only removes the cache's reference.<br/>\r\n Meaning, in-flight requests and subscriptions holding the evicted `Arc<SchemaState>` continue safely.\r\n- Selecting an evicted document again rebuilds its state.\r\n- A failed build is not cached and does not evict a valid entry.\r\n- A failed build returns HTTP 500 instead of falling back to the default schema, because fallback could expose fields the plugin intended to remove or change.\r\n\r\nMiss construction is serialized so concurrent first requests cannot build the same cached state more than once.\r\n\r\n### Drop `with_schema` from the `on_graphql_validation` plugin hook\r\n\r\nRemove `OnGraphQLValidationStartHookPayload::with_schema`. Method was broken by design, it replaced the schema only for validation while parsing, introspection, normalization, planning, and execution continued using the request's original schema state. This could make a field disappear during validation while remaining visible through introspection, and it could leave schema-derived caches and planning state inconsistent with the schema used to validate the operation.\r\n\r\nPlugins that need a request-specific schema should now build and retain a stable `Arc<Document>` for each schema variant and select it in `on_http_request`:\r\n\r\n```rust\r\nfn on_http_request<'req>(\r\n &'req self,\r\n payload: OnHttpRequestHookPayload<'req>,\r\n) -> OnHttpRequestHookResult<'req> {\r\n payload.set_schema_document(self.document_for_request(&payload).clone());\r\n payload.proceed()\r\n}\r\n```\r\n\r\n### Stable document responsibility\r\n\r\nPlugins must construct each schema variant once and retain the same `Arc<Document>` in their own state. Parsing a document or allocating a new `Arc<Document>` per request defeats cache reuse and triggers an expensive planner build for every request.\r\n\r\nFor example:\r\n\r\n```rust\r\npub struct ReplaceSchemaPlugin {\r\n variants: HashMap<&'static str, Arc<Document>>,\r\n}\r\n\r\nfn on_http_request<'req>(\r\n &'req self,\r\n payload: OnHttpRequestHookPayload<'req>,\r\n) -> OnHttpRequestHookResult<'req> {\r\n if let Some(document) = self.variants.get(\"basic\") {\r\n payload.set_schema_document(document.clone());\r\n }\r\n\r\n payload.proceed()\r\n}\r\n```\r\n\r\nSee [`plugin_examples/replace_schema`](https://github.com/graphql-hive/router/tree/main/plugin_examples/replace_schema) for a complete example that builds stable feature-specific documents and selects one from a request header.\r\n\r\n### Caveats for plugin authors\r\n\r\n- Building a `SchemaState` includes a full query planner build. The first request for each document therefore has additional latency.\r\n- Router-side supergraph reloads do not mutate or invalidate cached plugin-selected states and do not force-close subscriptions using them.\r\n- `on_http_request` is synchronous. External feature or project lookups must be refreshed asynchronously in the plugin lifecycle so the hook only performs a synchronous lookup from request data to a stable `Arc<Document>`.\r\n\r\n### TODO\r\n\r\n- [ ] plugin-selected states do not register callback heartbeat enforcer background task\r\n- [ ] document schema state cache has a max cap of 10, is this ok? should it be configurable? should the plugin author be able to manually evict?\r\n- [ ] docs\r\n",
"changed_files": 19,
"closed_at": null,
"comments": 3,
"comments_url": "https://api.github.com/repos/graphql-hive/router/issues/1269/comments",
"commits": 14,
"commits_url": "https://api.github.com/repos/graphql-hive/router/pulls/1269/commits",
"created_at": "2026-07-09T15:33:43Z",
"deletions": 396,
"diff_url": "https://github.com/graphql-hive/router/pull/1269.diff",
"draft": false,
"head": {
"label": "graphql-hive:super-replace",
"ref": "super-replace",
"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": 16,
"forks_count": 16,
"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": 60,
"open_issues_count": 60,
"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-07-15T09:59:43Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10342,
"squash_merge_commit_message": "PR_BODY",
"squash_merge_commit_title": "PR_TITLE",
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 94,
"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-07-15T09:09:50Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"use_squash_pr_title_as_default": true,
"visibility": "public",
"watchers": 94,
"watchers_count": 94,
"web_commit_signoff_required": false
},
"sha": "75641294d76f286ea7b1d8f00009adb8603a22ed",
"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/1269",
"id": 4023143227,
"issue_url": "https://api.github.com/repos/graphql-hive/router/issues/1269",
"labels": [],
"locked": false,
"maintainer_can_modify": false,
"merge_commit_sha": "d95402bc12238b622fc3d7dbfac56f34328772ef",
"mergeable": null,
"mergeable_state": "unknown",
"merged": false,
"merged_at": null,
"merged_by": null,
"milestone": null,
"node_id": "PR_kwDONSTNFM7vzEs7",
"number": 1269,
"patch_url": "https://github.com/graphql-hive/router/pull/1269.patch",
"rebaseable": null,
"requested_reviewers": [
{
"avatar_url": "https://avatars.githubusercontent.com/u/3680083?v=4",
"events_url": "https://api.github.com/users/dotansimha/events{/privacy}",
"followers_url": "https://api.github.com/users/dotansimha/followers",
"following_url": "https://api.github.com/users/dotansimha/following{/other_user}",
"gists_url": "https://api.github.com/users/dotansimha/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/dotansimha",
"id": 3680083,
"login": "dotansimha",
"node_id": "MDQ6VXNlcjM2ODAwODM=",
"organizations_url": "https://api.github.com/users/dotansimha/orgs",
"received_events_url": "https://api.github.com/users/dotansimha/received_events",
"repos_url": "https://api.github.com/users/dotansimha/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/dotansimha/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dotansimha/subscriptions",
"type": "User",
"url": "https://api.github.com/users/dotansimha",
"user_view_type": "public"
},
{
"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"
}
],
"requested_teams": [],
"review_comment_url": "https://api.github.com/repos/graphql-hive/router/pulls/comments{/number}",
"review_comments": 9,
"review_comments_url": "https://api.github.com/repos/graphql-hive/router/pulls/1269/comments",
"state": "open",
"statuses_url": "https://api.github.com/repos/graphql-hive/router/statuses/75641294d76f286ea7b1d8f00009adb8603a22ed",
"title": "feat(router,executor): allow plugins to override the supergraph from on_http_request",
"updated_at": "2026-07-15T09:59:45Z",
"url": "https://api.github.com/repos/graphql-hive/router/pulls/1269",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/11807600?v=4",
"events_url": "https://api.github.com/users/enisdenjo/events{/privacy}",
"followers_url": "https://api.github.com/users/enisdenjo/followers",
"following_url": "https://api.github.com/users/enisdenjo/following{/other_user}",
"gists_url": "https://api.github.com/users/enisdenjo/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/enisdenjo",
"id": 11807600,
"login": "enisdenjo",
"node_id": "MDQ6VXNlcjExODA3NjAw",
"organizations_url": "https://api.github.com/users/enisdenjo/orgs",
"received_events_url": "https://api.github.com/users/enisdenjo/received_events",
"repos_url": "https://api.github.com/users/enisdenjo/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/enisdenjo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/enisdenjo/subscriptions",
"type": "User",
"url": "https://api.github.com/users/enisdenjo",
"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": 16,
"forks_count": 16,
"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": 60,
"open_issues_count": 60,
"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-07-15T09:59:43Z",
"releases_url": "https://api.github.com/repos/graphql-hive/router/releases{/id}",
"size": 10342,
"ssh_url": "git@github.com:graphql-hive/router.git",
"stargazers_count": 94,
"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-07-15T09:09:50Z",
"url": "https://api.github.com/repos/graphql-hive/router",
"visibility": "public",
"watchers": 94,
"watchers_count": 94,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/11807600?v=4",
"events_url": "https://api.github.com/users/enisdenjo/events{/privacy}",
"followers_url": "https://api.github.com/users/enisdenjo/followers",
"following_url": "https://api.github.com/users/enisdenjo/following{/other_user}",
"gists_url": "https://api.github.com/users/enisdenjo/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/enisdenjo",
"id": 11807600,
"login": "enisdenjo",
"node_id": "MDQ6VXNlcjExODA3NjAw",
"organizations_url": "https://api.github.com/users/enisdenjo/orgs",
"received_events_url": "https://api.github.com/users/enisdenjo/received_events",
"repos_url": "https://api.github.com/users/enisdenjo/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/enisdenjo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/enisdenjo/subscriptions",
"type": "User",
"url": "https://api.github.com/users/enisdenjo",
"user_view_type": "public"
}
},
"github_job": "docker",
"github_ref": "refs/pull/1269/merge",
"github_ref_name": "1269/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": "29406540482",
"github_run_number": "4403",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20260705.232.1",
"github_runner_name": "GitHub Actions 1000911136",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_30c2069b-a5b1-481e-81ed-308c60248a9d",
"github_server_url": "https://github.com",
"github_triggering_actor": "enisdenjo",
"github_workflow": "build-router",
"github_workflow_ref": "graphql-hive/router/.github/workflows/build-router.yaml@refs/pull/1269/merge",
"github_workflow_sha": "05c61de1c1c019a12095dfd437e446628711e70c",
"platform": "linux/amd64"
}
}
},
"buildx.build.ref": "builder-557c61a2-28fc-4451-b3dc-1a5fca9b8d0a/builder-557c61a2-28fc-4451-b3dc-1a5fca9b8d0a0/f0uej01581asualj6pzo86wr9",
"containerimage.descriptor": {
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:4347ea0f7cd1f71bcf759a6c2c3c989e3044fe91de055564fbf2715420f1f794",
"size": 1609
},
"containerimage.digest": "sha256:4347ea0f7cd1f71bcf759a6c2c3c989e3044fe91de055564fbf2715420f1f794",
"image.name": "ghcr.io/graphql-hive/router:pr-1269,ghcr.io/graphql-hive/router:sha-05c61de"
} |
|
If the goal is to allow users to override schema for the entire execution, then what's the story behind allowing user to provide |
|
there's literally a section "Why override the whole SchemaState and not just SupergraphData?" in the PR's description. I did some rewording I hope it's clearer now |
This PR is being superseded by #1284
Description
Allow a plugin to select a supergraph document during
on_http_requestand have it hold for the entire request pipeline, including validation, normalization, planning, execution, and introspection. Previously, plugins could only replace the validation schema throughon_graphql_validation, so fields hidden from validation could still appear through__schemaand__typeintrospection.The new
on_http_requesthook API accepts a schema document:The plugin owns stable
Arc<Document>schema variants. After allon_http_requesthooks run, the router resolves the selected document into an internalArc<SchemaState>and stores that state in request extensions. The existing HTTP and WebSocket entry points then use that request-specific state for the full operation lifecycle.Why resolve a whole
SchemaStateinternally?The request pipeline consumes both:
SupergraphDataThese caches are not all keyed by the schema checksum. Replacing only
SupergraphDatacould therefore allow schema B to reuse normalized operations, query plans, or demand-control formulas produced under schema A.Each selected document is instead resolved to its own
SchemaState, which contains its ownSupergraphData, query planner, caches, demand-control runtime, and callback subscriptions map. This keeps schema-derived state isolated while letting repeated requests reuse the expensive planner build. The request-deduplication fingerprint already includesschema_checksum, so request deduplication also remains isolated across schema variants.Keeping
SchemaStateinternal also avoids exposing router configuration and telemetry internals to plugins. The router builds each state with its actualHiveRouterConfigandTelemetryContext.Schema-state cache
The router keeps up to 10 plugin-selected schema states in a fixed-size cache:
Arcallocation identity, usingArc::ptr_eq.SchemaStatesynchronously.Arc<Document>reuse the existing state.Meaning, in-flight requests and subscriptions holding the evicted
Arc<SchemaState>continue safely.Miss construction is serialized so concurrent first requests cannot build the same cached state more than once.
Drop
with_schemafrom theon_graphql_validationplugin hookRemove
OnGraphQLValidationStartHookPayload::with_schema. Method was broken by design, it replaced the schema only for validation while parsing, introspection, normalization, planning, and execution continued using the request's original schema state. This could make a field disappear during validation while remaining visible through introspection, and it could leave schema-derived caches and planning state inconsistent with the schema used to validate the operation.Plugins that need a request-specific schema should now build and retain a stable
Arc<Document>for each schema variant and select it inon_http_request:Stable document responsibility
Plugins must construct each schema variant once and retain the same
Arc<Document>in their own state. Parsing a document or allocating a newArc<Document>per request defeats cache reuse and triggers an expensive planner build for every request.For example:
See
plugin_examples/replace_schemafor a complete example that builds stable feature-specific documents and selects one from a request header.Caveats for plugin authors
SchemaStateincludes a full query planner build. The first request for each document therefore has additional latency.on_http_requestis synchronous. External feature or project lookups must be refreshed asynchronously in the plugin lifecycle so the hook only performs a synchronous lookup from request data to a stableArc<Document>.TODO