Docs
Launch GraphOS Studio

GraphQL subscriptions in cloud supergraphs

Real-time data delivery across your services


Cloud support for is currently in preview.

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

Cloud provide preview support for :

subscription OnStockPricesChanged {
stockPricesChanged {
symbol
price
}
}

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

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

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

NOTE

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

What are subscriptions for?

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

GraphOS RouterGraphQL ClientGraphOS RouterGraphQL ClientNew data availableNew data availableInitiates subscriptionSends new dataSends new data

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 subscriptions work

Subscribes
over WebSocket
(or via callback)
Can query for
entity fields
as needed
Subscribes
over HTTP
Client
GraphOS
Router
Stocks
subgraph
Portfolios
subgraph

  1. A client executes a against your over HTTP:

    Example subscription
    subscription OnStockPricesChanged {
    stockPricesChanged {
    symbol
    price
    }
    }
    • The client doesn't use a WebSocket protocol. Instead, it receives updates via multipart HTTP responses.
    • By using HTTP for , clients can execute all types over HTTP instead of using two different protocols.
    • Apollo Client, Apollo Kotlin, and Apollo iOS all support over HTTP with minimal configuration. See each library's documentation for details. also provides network adapters for the Relay and urql libraries.
  2. When your receives a , it executes that same subscription against whichever defines the requested stockPricesChanged in the code snippet above.

    • This communication usually does use a WebSocket subprotocol, for compatibility with most libraries.
    • With a , you can also configure an HTTP-callback-based protocol.
  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 that are defined in other . If it does, the first fetches those fields by the corresponding subgraphs, such as the Portfolios subgraph in the diagram above. These queries use HTTP as usual.

Prerequisites

Before you add Subscription to your , 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 .
  3. If your 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 that don't define any Subscription .
  4. In each with , make sure the subgraph uses the graphql-transport-ws WebSocket protocol for .

  5. In each with , 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 on your directly using the original path.
  6. Deploy your updated .

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

Default configuration

are enabled automatically for with the following default configuration:

subscription:
enabled: true
mode:
passthrough:
all:
path: /ws

Example execution

Let's say our includes the following 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 against your over HTTP!

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

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 of the Product to merge entity from across .
  • This removes all 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 from the Reviews subgraph!

Instead, our executes a standard against the Reviews to fetch the missing :

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

© 2024 Apollo Graph Inc.

Privacy Policy

Company