-
Notifications
You must be signed in to change notification settings - Fork 683
Description
Error handling is an important aspect of a client library and as we’re looking at 4.0, we think it can be improved and are seeking feedback.
Exceptions vs GraphQL errors
2 kinds of errors need to be handled:
- Non-GraphQL errors: typically network issues and cache misses
- GraphQL errors: an operation has correctly been executed and a result has been received, and it contains a non empty
errorsfield
Current situation
Currently, non-GraphQL errors are surfaced by throwing exceptions, whereas GraphQL errors are returned in ApolloResponse.errors. (See v3 doc error section).
Some drawbacks:
- These 2 different ways make it difficult to handle errors at the same area of the code
- Exceptions must be caught or they will crash your application
- Exceptions are terminal in Flows:
- surfacing first a cache miss and then a network result (e.g. when using CacheFirst strategy) is not straightforward
Proposed change
Similarly to ApolloResponse.errors, a new field ApolloResponse.exception: ApolloException is added, and the response is emitted (.toFlow()) or returned (.execute()), instead of throwing.
execute():
| Before |
try {
val response = client.query(MyQuery()).execute()
if (response.data != null) {
// Handle (potentially partial) data
} else {
// Handle GraphQL errors
}
} catch (e: ApolloException) {
// Handle network error
} |
| After |
val response = client.query(MyQuery()).execute()
if (response.data != null) {
// Handle (potentially partial) data
} else {
// Something wrong happened
if (response.exception != null) {
// Handle non-GraphQL errors
} else {
// Handle GraphQL errors
}
} |
.toFlow():
| Before |
client.subscription(MySubscription()).toFlow()
.catch { e ->
// Handle network error
}
.collect { response ->
if (response.data != null) {
// Handle (potentially partial) data
} else {
// Handle GraphQL errors
}
} |
| After |
client.subscription(MySubscription()).toFlow().collect { response ->
if (response.data != null) {
// Handle (potentially partial) data
} else {
// Something wrong happened
if (response.exception != null) {
// Handle non-GraphQL errors
} else {
// Handle GraphQL errors
}
}
} |
This addresses the drawbacks above, and arguably is a more consistent API - at the cost of a breaking change. To ease the transition, methods that throw like in 3.x will be added. Projects will be able to use them temporarily to keep the old behavior and migrate to the new one progressively.
Partial data and nullability
In the area of error handling, pain points related to handling GraphQL errors and nullability have also been identified:
- Some projects don't need to handle partial data, or only at a few specific places
- Many projects have a lot of nullable fields in their schemas that in reality can rarely be
null(only in error cases)
To improve usability around this, v4 will introduce:
- Error aware parsing: GraphQL errors are handled at parse time and
datais guaranteed null when there are errors / non-null otherwise @catchandFieldResultto handle partial data for specific fields@semanticNonNullto make fields generated as non null
This is introduced in #5405, and documentation is in #5407.
Feedback
Please comment on this ticket with any feedback you may have!