From 966802d1544c92a1d8a6baf9599aeaa4241f08c1 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Tue, 6 Jan 2026 20:07:34 +0100 Subject: [PATCH 1/3] federated subscriptions --- .../docs/src/content/router/guides/_meta.ts | 1 + .../content/router/guides/subscriptions.mdx | 437 ++++++++++++++++++ .../web/docs/src/content/router/index.mdx | 2 + 3 files changed, 440 insertions(+) create mode 100644 packages/web/docs/src/content/router/guides/subscriptions.mdx diff --git a/packages/web/docs/src/content/router/guides/_meta.ts b/packages/web/docs/src/content/router/guides/_meta.ts index 61130150e84..c961c8d25ec 100644 --- a/packages/web/docs/src/content/router/guides/_meta.ts +++ b/packages/web/docs/src/content/router/guides/_meta.ts @@ -2,4 +2,5 @@ export default { 'dynamic-subgraph-routing': 'Dynamic Subgraph Routing', 'header-manipulation': 'Header Manipulation', 'performance-tuning': 'Performance Tuning & Traffic Shaping', + subscriptions: 'Federated Subscriptions', }; diff --git a/packages/web/docs/src/content/router/guides/subscriptions.mdx b/packages/web/docs/src/content/router/guides/subscriptions.mdx new file mode 100644 index 00000000000..da02d6b9d5d --- /dev/null +++ b/packages/web/docs/src/content/router/guides/subscriptions.mdx @@ -0,0 +1,437 @@ +--- +title: 'Federated Subscriptions' +--- + +import { Callout } from '#components/callout' +import { Tabs } from '@theguild/components' + +# Federated Subscriptions + +GraphQL subscriptions enable your clients to receive real-time data. This is essential for +applications that need live updates - like notifications, live chat, stock tickers, collaborative +editing, or IoT dashboards. + +Hive Router provides full support for federated subscriptions out of the box, with no additional +configuration required. It works as a drop-in replacement for other federation routers, making it +easy to add real-time capabilities to your federated GraphQL architecture. + +## How Subscriptions Work + +Consider this subscription query against Hive Router: + +```graphql +subscription { + reviewAdded { + rating + body + author { + name + } + } +} +``` + +This creates the following data flow: + +```mermaid +flowchart LR; + client(Client); + router(["Hive Router"]); + subgraphA[Reviews
subgraph]; + subgraphB[Users
subgraph]; + router-->|"Subscribes
via reviewAdded"|subgraphA; + router-.->|Queries for
author entity fields|subgraphB; + client-->|"Subscribes"|router; + class client secondary; +``` + +Your client executes a subscription operation against Hive Router over HTTP using either Server-Sent +Events (SSE) or multipart responses. The router then executes the same subscription against +whichever subgraph defines the requested field - in this case, the Reviews subgraph that provides +the `reviewAdded` field. The router communicates with subgraphs using the same protocols: SSE or +multipart. + +When the Reviews subgraph sends new data, the router receives it and forwards it to the client. If +the subscription includes federated entity fields from other subgraphs - like the `author` field +that comes from the Users subgraph in this example - the router automatically resolves those fields +by querying the corresponding subgraphs over HTTP. This allows you to subscribe to data that spans +multiple subgraphs while maintaining a single subscription connection from your client. + +## Supported Protocols + +Hive Router currently supports two protocols for subscriptions, both for client-to-router and +router-to-subgraph communication: + +- [Server-Sent Events (SSE)](#server-sent-events-sse) - A simple, efficient protocol built on + standard HTTP +- [Incremental Delivery over HTTP](#incremental-delivery) - From the + [official GraphQL over HTTP spec RFC](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md). +- [Multipart HTTP](#multipart-http) - Based on + [Apollo's Multipart HTTP protocol](https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/multipart-protocol) + + + +[GraphQL over WebSocket](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) and +[HTTP Callback (for subgraphs)](https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/callback-protocol) +support is coming soon. We want to ensure they meet our standards for reliability and performance +before making them generally available. + + + +When dealing with HTTP subscriptions, the router automatically determines which protocol to use +based on the `Accept` header in the request. When subscribing to subgraphs, the router prefers +multipart first, then falls back to SSE. + +## Entity Resolution + +One of the most powerful features of federated subscriptions is automatic entity resolution. Your +subscription can include fields from multiple subgraphs, and Hive Router handles all the +coordination automatically. + +```graphql +subscription { + reviewAdded { + # From the reviews subgraph + id + body + rating + # Entity field from the products subgraph + product { + name + price + } + # Entity field from the users subgraph + author { + name + email + } + } +} +``` + +In this example: + +- The `reviewAdded` subscription is defined in the **reviews** subgraph +- The `product` fields are resolved from the **products** subgraph +- The `author` fields are resolved from the **users** subgraph + +Hive Router intelligently determines the optimal way to resolve these entity fields, querying the +necessary subgraphs as subscription events arrive. This works exactly like entity resolution for +regular queries - no special configuration or considerations needed. + +## Server-Sent Events (SSE) + +To use SSE, clients should send requests with the following `Accept` header: + +```text +Accept: text/event-stream +``` + +The router will respond with a stream of events implementing +[the "distinct subscriptions mode" of the GraphQL over SSE spec](https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#distinct-connections-mode) +. + +### Try It + +```bash +curl 'http://localhost:4000/graphql' \ + -H 'accept: text/event-stream' \ + --json '{ + "query": "subscription { + reviewAdded { + body + rating + product { + name + } + author { + name + } + } + }" + }' +``` + +This command creates an SSE connection and keeps it open to receive new subscription data as +server-sent events: + +```text +event: next +data: {"data":{"reviewAdded":{"body":"Great product!","rating":5,"product":{"name":"Croissant"},"author":{"name":"Alice"}}}} + +event: next +data: {"data":{"reviewAdded":{"body":"Could be better","rating":3,"product":{"name":"Baguette"},"author":{"name":"Bob"}}}} + +event: next +data: {"data":{"reviewAdded":{"body":"Excellent quality","rating":5,"product":{"name":"Croissant"},"author":{"name":"Charlie"}}}} + +event: complete +``` + +This example subscription emits three `next` events and then sends a `complete` event to signal the +end of the stream. Notice how the `product` and `author` fields are automatically resolved from +their respective subgraphs. + +### EventSource + +You can easily subscribe over SSE using the +[browser native `EventSource` interface](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). +It's as simple as: + +```ts +const url = new URL('http://localhost:4000/graphql') +url.searchParams.append( + 'query', + `subscription { + reviewAdded { + body + rating + product { + name + } + author { + name + } + } + }` +) + +const source = new EventSource(url) + +source.addEventListener('next', ({ data }) => { + console.log(data) +}) + +source.addEventListener('error', e => { + console.error(e) +}) + +source.addEventListener('complete', () => { + source.close() +}) +``` + +### graphql-sse + +We highly recommend using [`graphql-sse`](https://the-guild.dev/graphql/sse), the zero-dependency, +HTTP/1 safe, simple, GraphQL over Server-Sent Events spec client library. + +It offers reliable handling of connection management, message parsing, and error handling, as well +as silent retries and exponential backoff for improved resilience. + +#### Apollo Client + +Please advise the +[Apollo Client integration recipe](https://the-guild.dev/graphql/sse/recipes#with-apollo). + +#### Relay + +Please advise the [Relay integration recipe](https://the-guild.dev/graphql/sse/recipes#with-relay). + +#### urql + +Please advise the [urql integration recipe](https://the-guild.dev/graphql/sse/recipes#with-urql). + + + +urql also supports SSE out of the box! Just make sure to +[enable subscriptions over fetch](https://nearform.com/open-source/urql/docs/advanced/subscriptions/#using-fetch-for-subscriptions). + + + +## Incremental Delivery + +Using incremental delivery requires clients to send requests with the following `Accept` header: + +```text +Accept: multipart/mixed +``` + +The router responds with multipart HTTP responses conforming to +[the Offical GraphQL over HTTP Incremental Delivery over HTTP RFC](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md). + +### Apollo Client + + + +Please instead advise the [multipart HTTP example](#apollo-client-2) since Apollo Client supports +multipart HTTP out-of-the-box. + + + +### Relay + +[`meros`](https://github.com/maraisr/meros) is a fast utility that makes reading multipart responses +simple. It works in both the browser and Node.js. + +Using it with Relay is quite straight forward, here's an example setup: + +```npm2yarn +npm install meros +``` + +```ts +import { meros } from 'meros/browser' +import { GraphQLResponse, Network, Observable, RequestParameters, Variables } from 'relay-runtime' + +// Supports both queries/mutations and subscriptions. +function fetchOrSubscribe(operation: RequestParameters, variables: Variables) { + return Observable.create(sink => { + const ctrl = new AbortController() + + ;(async () => { + const parts = await fetch('http://localhost:4000/graphql', { + signal: ctrl.signal, + method: 'POST', + body: JSON.stringify({ + query: operation.text, + variables + }), + headers: { + 'content-type': 'application/json', + accept: 'application/json, multipart/mixed' + } + }).then(meros) + + if (parts instanceof Response) { + // query/mutation + return parts.json() + } + + // subscription + for await (const part of parts) { + sink.next(part) + } + })() + .then(() => sink.complete()) + .catch(error => sink.error(error)) + + return () => ctrl.abort() + }) +} + +export const network = Network.create(fetchOrSubscribe, fetchOrSubscribe) +``` + + + +Alternatively, you can also use +[`fetch-multipart-graphql`](https://github.com/relay-tools/fetch-multipart-graphql) for Relay +clients to add support incremental delivery over HTTP. It's a library maintained by Relay Tools - +community-built tools for working with Relay. + + + +### urql + + + +urql supports incremental delivery over HTTP out of the box! Just make sure to +[enable subscriptions over fetch](https://nearform.com/open-source/urql/docs/advanced/subscriptions/#using-fetch-for-subscriptions). + + + +## Multipart HTTP + +To use multipart responses, clients should send requests with the following `Accept` header: + +```text +Accept: multipart/mixed;subscriptionSpec=1.0 +``` + +The router will respond with multipart HTTP responses conforming to +[Apollo's Multipart HTTP protocol](https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/multipart-protocol). + +### Try It + +```bash +curl 'http://localhost:4000/graphql' \ + -H 'accept: multipart/mixed;subscriptionSpec=1.0' \ + --json '{ + "query": "subscription { + reviewAdded { + body + rating + product { + name + } + author { + name + } + } + }" + }' +``` + +This command creates an HTTP multipart request and keeps an open connection that receives new +subscription data in response "chunks": + +```text +--graphql +content-type: application/json + +{} +--graphql +content-type: application/json + +{"payload":{"data":{"reviewAdded":{"body":"Great product!","rating":5,"product":{"name":"Croissant"},"author":{"name":"Alice"}}}}} +--graphql +content-type: application/json + +{"payload":{"data":{"reviewAdded":{"body":"Could be better","rating":3,"product":{"name":"Baguette"},"author":{"name":"Bob"}}}}} +--graphql +content-type: application/json + +{"payload":{"data":{"reviewAdded":{"body":"Excellent quality","rating":5,"product":{"name":"Croissant"},"author":{"name":"Charlie"}}}}} +--graphql-- +``` + +This example subscription emits three events and then closes the connection. Notice how the +`product` and `author` fields are automatically resolved from their respective subgraphs. + +### Apollo Client + +The required headers are automatically added by Apollo Client. +[Multipart subscriptions are support out-of-the-box.](https://www.apollographql.com/docs/react/data/subscriptions#subscriptions-via-multipart-http) + +```npm2yarn +npm install @apollo/client graphql rxjs +``` + +```ts +import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' + +const client = new ApolloClient({ + link: new HttpLink({ uri: 'http://localhost:4000/graphql' }), + cache: new InMemoryCache() +}) + +client + .subscribe({ + query: gql` + subscription { + reviewAdded { + body + rating + product { + name + } + author { + name + } + } + } + ` + }) + .forEach(data => { + console.log(data) + }) +``` + +### Relay + +We recommend using [Incremental Delivery over HTTP with Relay instead](#relay-1). + +### urql + +We recommend using [Incremental Delivery over HTTP with urql instead](#urql-1). diff --git a/packages/web/docs/src/content/router/index.mdx b/packages/web/docs/src/content/router/index.mdx index bd8ce488fe9..b050f810bc6 100644 --- a/packages/web/docs/src/content/router/index.mdx +++ b/packages/web/docs/src/content/router/index.mdx @@ -46,5 +46,7 @@ Check out our compliance testing: - **[Getting Started](/docs/router/getting-started)** - Install and run the router in just a few minutes. +- **[Federated Subscriptions](/docs/router/guides/subscriptions)** - Enable real-time data updates + with GraphQL subscriptions. - **[Configuration Reference](/docs/router/configuration)** - Explore all the ways you can customize the router for your needs. From e47d6a5474b44f6ac27a6d6a16f6fc6f60475fe2 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Wed, 7 Jan 2026 22:18:54 +0100 Subject: [PATCH 2/3] unnecesary title --- packages/web/docs/src/content/router/guides/subscriptions.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/web/docs/src/content/router/guides/subscriptions.mdx b/packages/web/docs/src/content/router/guides/subscriptions.mdx index da02d6b9d5d..13af160afa8 100644 --- a/packages/web/docs/src/content/router/guides/subscriptions.mdx +++ b/packages/web/docs/src/content/router/guides/subscriptions.mdx @@ -1,7 +1,3 @@ ---- -title: 'Federated Subscriptions' ---- - import { Callout } from '#components/callout' import { Tabs } from '@theguild/components' From a58164975833b8934c9e7183f04cfbb0d5430cc0 Mon Sep 17 00:00:00 2001 From: enisdenjo Date: Wed, 7 Jan 2026 22:55:15 +0100 Subject: [PATCH 3/3] typos and stuff --- .../src/content/router/guides/subscriptions.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/web/docs/src/content/router/guides/subscriptions.mdx b/packages/web/docs/src/content/router/guides/subscriptions.mdx index 13af160afa8..dd19089cdd3 100644 --- a/packages/web/docs/src/content/router/guides/subscriptions.mdx +++ b/packages/web/docs/src/content/router/guides/subscriptions.mdx @@ -61,7 +61,7 @@ router-to-subgraph communication: - [Server-Sent Events (SSE)](#server-sent-events-sse) - A simple, efficient protocol built on standard HTTP - [Incremental Delivery over HTTP](#incremental-delivery) - From the - [official GraphQL over HTTP spec RFC](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md). + [Official GraphQL over HTTP spec RFC](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md) - [Multipart HTTP](#multipart-http) - Based on [Apollo's Multipart HTTP protocol](https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/multipart-protocol) @@ -244,7 +244,7 @@ Accept: multipart/mixed ``` The router responds with multipart HTTP responses conforming to -[the Offical GraphQL over HTTP Incremental Delivery over HTTP RFC](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md). +[the Official GraphQL over HTTP Incremental Delivery over HTTP RFC](https://github.com/graphql/graphql-over-http/blob/main/rfcs/IncrementalDelivery.md). ### Apollo Client @@ -260,7 +260,7 @@ multipart HTTP out-of-the-box. [`meros`](https://github.com/maraisr/meros) is a fast utility that makes reading multipart responses simple. It works in both the browser and Node.js. -Using it with Relay is quite straight forward, here's an example setup: +Using it with Relay is quite straightforward, here's an example setup: ```npm2yarn npm install meros @@ -313,8 +313,8 @@ export const network = Network.create(fetchOrSubscribe, fetchOrSubscribe) Alternatively, you can also use [`fetch-multipart-graphql`](https://github.com/relay-tools/fetch-multipart-graphql) for Relay -clients to add support incremental delivery over HTTP. It's a library maintained by Relay Tools - -community-built tools for working with Relay. +clients to add support for incremental delivery over HTTP. It's a library maintained by Relay +Tools - community-built tools for working with Relay. @@ -388,14 +388,14 @@ This example subscription emits three events and then closes the connection. Not ### Apollo Client The required headers are automatically added by Apollo Client. -[Multipart subscriptions are support out-of-the-box.](https://www.apollographql.com/docs/react/data/subscriptions#subscriptions-via-multipart-http) +[Multipart subscriptions are supported out-of-the-box.](https://www.apollographql.com/docs/react/data/subscriptions#subscriptions-via-multipart-http) ```npm2yarn npm install @apollo/client graphql rxjs ``` ```ts -import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client' +import { ApolloClient, gql, HttpLink, InMemoryCache } from '@apollo/client' const client = new ApolloClient({ link: new HttpLink({ uri: 'http://localhost:4000/graphql' }),