Here's your chance to speak at GraphQL Summit in New York City, October 8 - 10, 2024! 🏙️ Submit your proposal by May 31.
Docs
Launch GraphOS Studio
You're viewing documentation for an upcoming version of this software. Switch to the latest stable version.

Handle WebSocket errors


Because the WebSocket is a shared resource, error handling is different from regular queries. In HTTP queries, an error has a single consumer. With WebSockets, an error may have multiple consumers.

WebSocket errors with multiple consumers

When your network goes down or your server closes the connection (because it wants a new token or something else), all the are terminated. You could retry them individually. The example below uses a very naive exponential backoff algorithm:

apolloClient.subscription(MySubscription())
.toFlow()
.onEach {
// Throw the exception so we can use retryWhen
it.exception?.let { throw it }
}
.retryWhen { e, attempt ->
delay(2.0.pow(attempt.toDouble()).toLong())
// retry after the delay
true
}
.collect {
println("trips booked: ${it.data?.tripsBooked}")
}

This has a number of shortcomings (not mentioning the naive exponential backoff implementation):

  • This code has to be implemented in different places
  • The attempt is tied to the collector lifecycle, there is no way to reset it if the network is back up

Instead you can manage this in a more central place when configuring your ApolloClient:

val apolloClient = ApolloClient.Builder()
.httpServerUrl("http://localhost:8080/graphql")
.webSocketServerUrl("http://localhost:8080/subscriptions")
.webSocketReopenWhen { e, attempt ->
delay(2.0.pow(attempt.toDouble()).toLong())
// retry after the delay
true
}

The above code will remember all current subscriptions and resubscribe to the server automatically without having to call retry in each screen.

WebSocket errors with single consumers

In some cases, you may have a situation where an error is for a single consumer. This happens typically for error messages:

{
"id": "3f32558f-49a3-4dc2-9d1c-445adf0a66c7",
"type": "error",
"payload": { }
}

In these cases, the Flow will emit an ApolloResponse with a SubscriptionOperationException in its exception and then terminate. You can handle the retry like so:

apolloClient.subscription(MySubscription())
.toFlow()
.onEach {
// Throw the exception so we can use retryWhen
it.exception?.let { throw it }
}
.retryWhen { e, attempt ->
if (e is SubscriptionOperationException) {
// handle the operation error
} else {
// handle global errors that were not caught by webSocketReopenWhen
}
}
.collect {
println("trips booked: ${it.data?.tripsBooked}")
}
Previous
Authentication
Next
Batching operations
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company