Docs
Launch GraphOS Studio

Subscriptions in Apollo Kotlin


s are long-lived GraphQL read s that can update their response over time, enabling clients to receive new data as it becomes available.

The GraphQL spec does not specify a particular protocol to use for s. Apollo Kotlin supports the following protocols:

  • WebSocket, using one of the following subprotocols:
    • subscriptions-transport-ws (⚠️ not actively maintained!)
    • graphql-ws
    • appsync (also uses graphql-ws as Sec-WebSocket-Protocol)

You must use whichever transport is supported by your GraphQL endpoint.

You define a in your app just like you define a query, except you use the subscription keyword. Here's an example for getting the latest value of a number whenever that number is incremented:

subscription NumberIncremented {
numberIncremented
}

Unlike with queries and s, a can include only one of the Subscription type. To subscribe to multiple s, you create multiple s.

Configuring WebSocket subscriptions

By default, Apollo Kotlin uses the subscriptions-transport-ws protocol for s via the SubscriptionWsProtocol class. This protocol is no longer actively maintained. It remains the default for backward compatibility purposes.

A future version of Apollo Kotlin will change the default to the newer graphql-ws protocol and GraphQLWsProtocol class. If your server already uses graphql-ws, make sure to set your WsProtocol to GraphQLWsProtocol.

To use s over WebSocket, use WebSocketNetworkTransport:

val apolloClient = ApolloClient.Builder()
.subscriptionNetworkTransport(
WebSocketNetworkTransport.Builder()
.serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
.build()
)
.build()

Note: Apollo Kotlin supports both https:// (or http://) and wss:// (or ws://) protocols. Internally, wss:// is renamed to https:// and which one you use does not matter.

Customizing your WebSocket protocol

By default, Apollo Kotlin uses subscriptions-transport-ws for backward compatibility purposes, but it supports all of the following WebSocket subprotocols:

To customize your protocol, use the WsProtocol interface. Apollo Kotlin comes with built-in support for the subprotocols above:

SubprotocolClass
subscriptions-transport-wsSubscriptionWsProtocol (default)
graphql-wsGraphQLWsProtocol
appsyncAppSyncWsProtocol

For example, you can configure a graphql-ws transport like so:

val apolloClient = ApolloClient.Builder()
.subscriptionNetworkTransport(
WebSocketNetworkTransport.Builder()
.protocol(GraphQLWsProtocol.Factory())
.serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
.build()
)
.build()

AWS (Amplitude) AppSync

Configuring AppSync is easy but has some subtle nuances due to AppSync's authorization and domain requirements. In particular, AppSync s on custom domains must append "/realtime" to their endpoint. By contrast non-custom domains do not append "realtime" to the path and instead incorporate it into the domain.

private const val REALTIME = "realtime"
private fun wsUrl(url: String, auth: Map<String, String>): String {
val realtimeUrl = when {
url.contains(REALTIME) -> url
url.contains("amazonaws.com") -> {
val awsUrlParts = url.split("appsync-api")
require(awsUrlParts.size == 2) { "Invalid AWS url: $url" }
"${awsUrlParts[0]}appsync-realtime-api${awsUrlParts[1]}"
}
url.endsWith("/") -> "$url$REALTIME"
else -> "$url/$REALTIME"
}
return AppSyncWsProtocol.buildUrl(
baseUrl = realtimeUrl,
authorization = auth,
)
}
// assumes host and key variables have already been set
private fun wsAuth(): Map<String, String> = mapOf("host" to host, "x-api-key" to key)
val apolloClient = ApolloClient.Builder()
.subscriptionNetworkTransport(
WebSocketNetworkTransport.Builder()
// if you know that your URL is correct for realtime then below could be
// .serverUrl(AppSyncWsProtocol.buildUrl(url, wsAuth())
.serverUrl(wsUrl(url, wsAuth()))
.protocol(AppSyncWsProtocol.Factory(connectionPayload = { wsAuth() }))
.build(),
)
.build()

Authentication

Please refer to this section about authentication with WebSocket.

Configuring HTTP subscriptions

To use HTTP for s, use HttpNetworkTransport like so:

val apolloClient = ApolloClient.Builder()
.subscriptionNetworkTransport(
HttpNetworkTransport.Builder()
.serverUrl("https://apollo-fullstack-tutorial.herokuapp.com/graphql")
.build()
)
.build()

This is the only configuration required. HttpNetworkTransport will use chunked multipart responses for s and standard POST or GET requests for queries and s.

Listening to a subscription

After you configure the NetworkTransport, use ApolloClient.subscribe to open the connection and listen for changes:

apolloClient.subscription(TripsBookedSubscription())
.toFlow()
.collect {
println("trips booked: ${it.data?.tripsBooked}")
}

Because s are long-lasting s, they return a Flow<Response> instead of a single Response.

Terminating a subscription

Termination is handled through the coroutine scope. Cancel the coroutine to terminate the .

By default, a single WebSocket is shared between all active s. When no subscription is active, the WebSocket is closed after a configurable timeout.

Error handling

Like queries, s support partial responses with GraphQL errors, which are emitted in the Flow.

Network errors terminate the Flow, and you need to retry to get new updates. Depending on the situation, retrying might open a new WebSocket or restart the .

See also this section about WebSocket errors handling.

Previous
Mutations
Next
GraphQL variables
Edit on GitHubEditForumsDiscord