GraphQL Subscriptions in Cloud Supergraphs

Real-time data delivery across your services


Preview
Cloud supergraph support for GraphQL subscriptions is currently in preview.You can also use subscriptions with an Enterprise self-hosted supergraph. See the GraphOS Router documentation.

Cloud supergraphs provide preview support for GraphQL subscription operations:

GraphQL
1subscription OnStockPricesChanged {
2  stockPricesChanged {
3    symbol
4    price
5  }
6}

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

GraphQL
stocks.graphql
1type Subscription {
2  stockPricesChanged: [Stock!]!
3}

Clients can then execute subscriptions on your cloud router, which executes them on your subgraphs.

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 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 subscription operations.

  3. If your subgraph schemas specify an Apollo Federation version, modify them to use Apollo Federation 2.4 or later:

    GraphQL
    stocks.graphql
    1extend schema
    2  @link(
    3    url: "https://specs.apollo.dev/federation/v2.4"
    4    import: ["@key", "@shareable"]
    5  )
    6
    7type Subscription {
    8  stockPricesChanged: [Stock!]!
    9}
    • You can skip modifying subgraph schemas that don't define any Subscription fields.

  4. In each subgraph with subscriptions, make sure the subgraph uses the graphql-transport-ws WebSocket protocol for subscriptions.

  5. 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 /ws as 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.

  6. Deploy your updated subgraphs.

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

Default configuration

Subscriptions are enabled automatically for GraphOS Cloud with the following default router configuration:

YAML
1subscription:
2  enabled: true
3  mode:
4    passthrough:
5      all:
6        path: /ws

Example execution

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

GraphQL
Products subgraph
1type Product @key(fields: "id") {
2  id: ID!
3  name: String!
4  price: Int!
5}
6
7type Subscription {
8  productPriceChanged: Product!
9}
GraphQL
Reviews subgraph
1type Product @key(fields: "id") {
2  id: ID!
3  reviews: [Review!]!
4}
5
6type Review {
7  score: Int!
8}

A client can execute the following subscription against our router:

note
Remember, clients execute subscriptions against your router over HTTP!Apollo Client for Web, Kotlin, and iOS all support HTTP-based subscriptions.
GraphQL
1subscription OnProductPriceChanged {
2  productPriceChanged {
3    # Defined in Products subgraph
4    name
5    price
6    reviews {
7      # Defined in Reviews subgraph!
8      score
9    }
10  }
11}

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

GraphQL
1subscription {
2  productPriceChanged {
3    id # Added for entity fetching
4    name
5    price
6    # Reviews fields removed!
7  }
8}
note
  • This operation adds the Product.id field. The router needs @key fields of the Product entity 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:

GraphQL
1query {
2  _entities(representations: [...]) {
3    ... on Product {
4      reviews {
5        score
6      }
7    }
8  }
9}

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.

Trying subscriptions with curl

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:

Bash
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 subscription data in multiple response parts:

Text
--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 subscription only emits three events and then directly closes the connection.For more information on this multipart HTTP subscription protocol, see this article.
Feedback

Edit on GitHub

Forums