Docs
Launch GraphOS Studio
You're viewing documentation for an upcoming version of this software. Switch to the latest stable version.

Error handling


You can use response.data to check if the server returned valid data:

val response = apolloClient.query(ExampleQuery()).execute()
// Using 'data' to be able to do a smart cast below (KT-8819)
val data = response.data
when {
data != null -> {
// data can be smart cast to non-null here
println("The server returned data: $data")
}
else -> {
// an error happened, check response.exception for more details or just display a generic error
when (response.exception) {
is ApolloGraphQLException -> // do something with exception.errors
is ApolloHttpException -> // do something with exception.statusCode
is ApolloNetworkException -> TODO()
is ApolloParseException -> TODO()
else -> // generic error
}
}
}

This is also true when calling toFlow() (e.g. with subscriptions) and watch() (with the normalized cache).

apolloClient.subscription(subscription).toFlow().collect { response ->
if (response.data != null) {
// handle data
} else {
// handle exceptions
}
}

If you prefer throwing, you can use dataOrThrow() to get a non-null data:

val data = apolloClient.query(ExampleQuery()).execute().dataOrThrow()
// data is non-null

Different types of errors

Whenever you execute a GraphQL with Apollo Kotlin (or any other GraphQL client), two high-level types of errors can occur:

  • Network errors: a GraphQL response wasn't received because an error occurred while communicating with your GraphQL server. This might be an SSL error, a socket error because your app is offline, or a 500 or any other HTTP error. When a network error occurs, no data is returned.
  • GraphQL errors: a GraphQL response is received, and it contains a non-empty errors . This means the server wasn't able to completely process the query. The response might include partial data if the server was able to process some of the query.

GraphQL errors

For example, the following query uses an invalid id to look up a Person:

query FilmAndPersonQuery {
film(id: "ZmlsbXM6MQ==") {
title
}
person(id: "badId") {
name
}
}

The server will send the following response:

{
"data": {
"film": {
"title": "A New Hope"
},
"person": null
},
"errors": [
{
"message": "No entry in local cache for https://swapi.dev/api/people/m�H/",
"locations": [
{
"line": 35,
"column": 3
}
],
"path": [
"person"
]
}
]
}

Note that while there are errors, the query successfully returned the title of the film: A New Hope. In general, any error while executing an bubbles up to the next nullable . In this case, person is nullable. In the worst case, response.data can be null if everything else is non-nullable.

When GraphQL errors happen, ApolloResponse.exception will be an instance of ApolloGraphQLException and you can use exception.errors to read the actual GraphQL errors.

Network errors

Network errors are more varied and can have different causes (non-exhaustive list):

  • The app is offline or doesn't have access to the network.
  • A DNS error occurred, making it impossible to look up the host.
  • An SSL error occurred (e.g., the server certificate isn't trusted).
  • The connection was closed.
  • The server responded with a non-successful HTTP code.
  • The server didn't respond with valid JSON.
  • The response JSON doesn't satisfy the and cannot be parsed.
  • A request was specified as CacheOnly but the data wasn't cached.
  • More...

Ignoring partial data

Because errors may happen on any in your query, your UI code needs to handle any nullable field. In the example above, data.person will be null:

query FilmAndPersonQuery {
# wrong id here, null will be returned
person(id: "badId") {
name
}
}

Your UI code needs to check for null:

if (data.person != null) {
// display person
}

If you prefer to catch these errors at parsing time, you can make data.person non-nullable using the @nonnull directive:

extend type Query @nonnull(fields: "person")

Alternatively, you can check for errors in an interceptor and force data to null whenever a GraphQL error happens:

object ignorePartialDataInterceptor: ApolloInterceptor {
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
return chain.proceed(request).map {
if (it.exception is ApolloGraphQLException) {
// nullify data altogether
it.newBuilder().data(null).build()
} else {
it
}
}
}
}

Note that the null check is still required on person because this has a nullable type in the GraphQL .

Previous
GraphQL variables
Next
Custom scalars
Edit on GitHubEditForumsDiscord