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 s:

subscription OnStockPricesChanged {
stockPricesChanged {
symbol
price
}
}

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

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

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

NOTE

To use s with your cloud , you must first complete certain prerequisites.

What are subscriptions for?

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 :

Cloud RouterGraphQL ClientCloud RouterGraphQL ClientNew data availableNew data availableInitiates subscriptionSends new dataSends new data

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 against your 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 types over HTTP instead of using two different protocols.
    • for Web, Kotlin, and iOS all support s over HTTP with minimal configuration. See each library's documentation for details.
  2. When your 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 ing 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 the following in the order shown to prevent errors:

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

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

    • Previous versions of don't support s.
  3. If your s specify an 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 .

Example execution

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

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 :

NOTE

Remember, clients execute subscriptions against your router over HTTP!

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

  • 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 query against the Reviews to fetch the missing entity s:

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

After receiving this 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 library, you can execute a curl command against your 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--

NOTE

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