Docs
Launch GraphOS Studio

GraphQL subscriptions with a cloud supergraph

Real-time data delivery from across your services


Cloud supergraph support for GraphQL subscriptions is currently in preview.

You can also use s with an Enterprise self-hosted . See the Apollo Router documentation.

Cloud s provide preview support for GraphQL s:

subscription OnStockPricesChanged {
stockPricesChanged {
symbol
price
}
}

With a cloud , you can add Subscription s to the of any that supports the graphql-transport-ws WebSocket protocol:

stocks.graphql
type Subscription {
stockPricesChanged: [Stock!]!
}

Clients can then execute s on your cloud , which executes them on your s.

⚠️ Important: To use s with your cloud , you must first complete certain prerequisites.

What are subscriptions for?

GraphQL s enable clients to receive continual, real-time updates whenever new data becomes available. Unlike queries and s, subscriptions are long-lasting. This means a client can receive multiple updates from a single :

GraphQL ClientCloud RouterInitiates subscriptionNew data availableSends new dataNew data availableSends new dataGraphQL ClientCloud Router

s are best suited to apps that rely on frequently-changing, time-sensitive data (such as stock prices, IoT sensor readings, live chat, or sports scores).

How it works

Your infrastructure
GraphOS
Subscribes
over WebSocket
Can query for
entity fields
as needed
Subscribes
over HTTP
Stocks
subgraph
Portfolios
subgraph
Cloud
Router
Client
  1. A client executes a GraphQL against your cloud over HTTP:

    Example subscription
    subscription OnStockPricesChanged {
    stockPricesChanged {
    symbol
    price
    }
    }
    • The client does not use a WebSocket protocol! Instead, it receives updates via multipart HTTP responses.
    • By using HTTP for s, clients can execute all GraphQL types over HTTP instead of using two different protocols.
    • Apollo Client for Web, Kotlin, and iOS all support GraphQL s over HTTP with minimal configuration. See each library's documentation for details.
  2. When your cloud receives a , it executes that same against whichever defines the requested (stockPricesChanged in the example above).

  3. The periodically sends new data to your . Whenever it does, the router returns that data to the client in an additional HTTP response part.

    • A can include federated entity s that are defined in other s. If it does, the first fetches those s by querying the corresponding s (such as Portfolios in the diagram above). These queries use HTTP as usual.

Prerequisites

⚠️ Before you add Subscription fields to your subgraphs, do all of the following in the order shown to prevent errors:

  1. Make sure you've created a cloud supergraph and connected your GraphQL API to it!

  2. Update your supergraph's build pipeline to use Apollo Federation 2.4 or later.

    • Previous versions of Apollo Federation don't support s.
  3. If your s specify an Apollo Federation version, modify them to use Apollo Federation 2.4 or later:

    stocks.graphql
    extend schema
    @link(url: "https://specs.apollo.dev/federation/v2.4",
    import: ["@key", "@shareable"])
    type Subscription {
    stockPricesChanged: [Stock!]!
    }
    • You can skip modifying s that don't define any Subscription s.
  4. In each with s, make sure the subgraph uses the graphql-transport-ws WebSocket protocol for s.

  5. In each with s, 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 /ws as an additional path instead of removing the original path. This is helpful if legacy clients will continue executing s on your directly using the original path.
  6. Deploy your updated s.

After you complete these prerequisites, you begin executing subscriptions on your cloud .

Example execution

Let's say our includes the following s and partial s:

Products subgraph
type Product @key(fields: "id") {
id: ID!
name: String!
price: Int!
}
type Subscription {
productPriceChanged: Product!
}
Reviews subgraph
type Product @key(fields: "id") {
id: ID!
reviews: [Review!]!
}
type Review {
score: Int!
}

A client can execute the following against our :

⚠️ Remember, clients execute subscriptions against your router over HTTP!

Apollo Client for Web, Kotlin, and iOS all support HTTP-based s.

subscription OnProductPriceChanged {
productPriceChanged {
# Defined in Products subgraph
name
price
reviews {
# Defined in Reviews subgraph!
score
}
}
}

When our receives this , it executes a corresponding operation against the Products (over a new WebSocket connection):

subscription {
productPriceChanged {
id # Added for entity fetching
name
price
# Reviews fields removed!
}
}

Note the following:

  • This adds the Product.id . The needs @key s of the Product entity to merge entity s from across s.
  • This removes all s defined in the Reviews , because the Products subgraph can't resolve them.

At any point after the is initiated, the Products might send updated data to our . Whenever this happens, the router does not immediately return this data to the client, because it's missing requested s from the Reviews !

Instead, our executes a standard GraphQL query against the Reviews to fetch the missing entity s:

query {
_entities(representations: [...]) {
... on Product {
reviews {
score
}
}
}
}

After receiving this query result from the Reviews , our combines it with the data from Products and returns the combination to the subscribing client.

Trying subscriptions with curl

To quickly try out HTTP-based s without setting up an Apollo Client library, you can execute a curl command against your cloud with the following format:

Example curl request
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 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 only emits three events and then directly closes the connection.

For more information on this multipart HTTP protocol, see this article.

Previous
Using @defer
Next
Platform API
Edit on GitHubEditForumsDiscord