| title | GraphQL Subscriptions in Cloud Supergraphs | |
|---|---|---|
| subtitle | Real-time data delivery across your services | |
| description | Cloud routers support GraphQL subscriptions by default, enabling clients to receive real-time updates via WebSocket or HTTP callbacks. | |
| redirectFrom |
|
|
| releaseStage | preview |
import CloudRouterDiscontinuationNotice from '../../../shared/cloud-router-discontinuation-notice.mdx';
Cloud supergraphs provide preview support for GraphQL subscription operations:
subscription OnStockPricesChanged {
stockPricesChanged {
symbol
price
}
}With a cloud supergraph, you can add Subscription fields to the schema of any subgraph that supports the graphql-transport-ws WebSocket protocol:
type Subscription {
stockPricesChanged: [Stock!]!
}Clients can then execute subscriptions on your cloud router, which executes them on your subgraphs.
Before you add Subscription fields to your subgraphs, do all the following in the order shown to prevent errors:
-
Make sure you've created a cloud supergraph and connected your GraphQL API to it.
-
Update your supergraph's build pipeline to use Apollo Federation 2.4 or later.
- Previous versions of Apollo Federation don't support subscription operations.
-
If your subgraph schemas specify an Apollo Federation version, modify them to use Apollo Federation 2.4 or later:
extend schema @link( url: "https://specs.apollo.dev/federation/v2.4" #highlight-line import: ["@key", "@shareable"] ) type Subscription { stockPricesChanged: [Stock!]! }
- You can skip modifying subgraph schemas that don't define any
Subscriptionfields.
- You can skip modifying subgraph schemas that don't define any
-
In each subgraph with subscriptions, make sure the subgraph uses the
graphql-transport-wsWebSocket protocol for subscriptions. -
In each subgraph with subscriptions, make sure the subgraph hosts its subscriptions WebSocket endpoint at the path
/ws.- If your WebSocket endpoint is currently hosted at a different path, you can add
/wsas an additional path instead of removing the original path. This is helpful if legacy clients will continue executing subscriptions on your subgraph directly using the original path.
- If your WebSocket endpoint is currently hosted at a different path, you can add
-
Deploy your updated subgraphs.
After you complete these prerequisites, you begin executing subscriptions on your cloud router.
Subscriptions are enabled automatically for GraphOS Cloud with the following default router configuration:
subscription:
enabled: true # Enabled by default, you don't need to add this to your configuration
mode:
passthrough:
all:
path: /wsLet's say our supergraph includes the following subgraphs and partial schemas:
type Product @key(fields: "id") {
id: ID!
name: String!
price: Int!
}
# highlight-start
type Subscription {
productPriceChanged: Product!
}
#highlight-endtype Product @key(fields: "id") {
id: ID!
reviews: [Review!]!
}
type Review {
score: Int!
}A client can execute the following subscription against our router:
Remember, clients execute subscriptions against your router over HTTP!
Apollo Client for Web, Kotlin, and iOS all support HTTP-based subscriptions.
subscription OnProductPriceChanged {
productPriceChanged {
# Defined in Products subgraph
name
price
reviews {
# Defined in Reviews subgraph!
score
}
}
}When our router receives this operation, it executes a corresponding subscription operation against the Products subgraph (over a new WebSocket connection):
subscription {
productPriceChanged {
id # Added for entity fetching
name
price
# Reviews fields removed!
}
}- This operation adds the
Product.idfield. The router needs@keyfields of theProductentity to merge entity fields from across subgraphs. - This operation removes all fields defined in the Reviews subgraph, because the Products subgraph can't resolve them.
At any point after the subscription is initiated, the Products subgraph might send updated data to our router. Whenever this happens, the router does not immediately return this data to the client, because it's missing requested fields from the Reviews subgraph!
Instead, our router executes a standard GraphQL query against the Reviews subgraph to fetch the missing entity fields:
query {
_entities(representations: [...]) {
... on Product {
reviews {
score
}
}
}
}After receiving this query result from the Reviews subgraph, our router combines it with the data from Products and returns the combination to the subscribing client.
To quickly try out HTTP-based subscriptions without setting up an Apollo Client library, you can execute a curl command against your cloud router with the following format:
curl 'https://main--my-org-supergraph.apollographos.net/graphql' -v \
-H 'accept: multipart/mixed; boundary="graphql"; subscriptionSpec=1.0, application/json' \
-H 'content-type: application/json' \
--data-raw '{"query":"subscription OnProductPriceChanged { productPriceChanged { name price reviews { score } } }","operationName":"OnProductPriceChanged"}'This command creates an HTTP multipart request and keeps an open connection that receives new subscription data in multiple response parts:
--graphql
content-type: application/json
{}
--graphql
content-type: application/json
{"payload":{"data":{"productPriceChanged":{"name":"Croissant","price":400,"reviews":[{"score":5}]}}}}
--graphql
content-type: application/json
{"payload":{"data":{"productPriceChanged":{"name":"Croissant","price":375,"reviews":[{"score":5}]}}}}
--graphql
content-type: application/json
{"payload":{"data":{"productPriceChanged":{"name":"Croissant","price":425,"reviews":[{"score":5}]}}}}
--graphql--
This example subscription only emits three events and then directly closes the connection.
For more information on this multipart HTTP subscription protocol, see this article.