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:
subscription {
reviewAdded {
rating
body
author {
name
}
}
}This creates the following data flow:
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) - A simple, efficient protocol built on standard HTTP
- Incremental Delivery over HTTP - From the official GraphQL over HTTP spec RFC.
- Multipart HTTP - Based on Apollo’s Multipart HTTP protocol
GraphQL over WebSocket and HTTP Callback (for subgraphs) 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.
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
reviewAddedsubscription is defined in the reviews subgraph - The
productfields are resolved from the products subgraph - The
authorfields 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:
Accept: text/event-streamThe router will respond with a stream of events implementing the “distinct subscriptions mode” of the GraphQL over SSE spec .
Try It
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:
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: completeThis 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.
It’s as simple as:
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, 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.
Relay
Please advise the Relay integration recipe.
urql
Please advise the urql integration recipe.
urql also supports SSE out of the box! Just make sure to enable subscriptions over fetch.
Incremental Delivery
Using incremental delivery requires clients to send requests with the following Accept header:
Accept: multipart/mixedThe router responds with multipart HTTP responses conforming to the Offical GraphQL over HTTP Incremental Delivery over HTTP RFC.
Apollo Client
Please instead advise the multipart HTTP example since Apollo Client supports multipart HTTP out-of-the-box.
Relay
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:
npm install merosimport { 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<GraphQLResponse>(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 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.
Multipart HTTP
To use multipart responses, clients should send requests with the following Accept header:
Accept: multipart/mixed;subscriptionSpec=1.0The router will respond with multipart HTTP responses conforming to Apollo’s Multipart HTTP protocol.
Try It
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”:
--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.
npm install @apollo/client graphql rxjsimport { 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.
urql
We recommend using Incremental Delivery over HTTP with urql instead.