How to catch exceptions from ApolloClient watch method?

258 Views Asked by At

This code - manual query execution without watch - throws an exception as I expect it (due to an intentionally wrong server url not shown here):

try {
    val response = apolloClient.query(MyQuery()).execute()
} catch (e: Exception) {
    Timber.e("exception from manual execution")
    Timber.e(e)
}

However, if instead I use watch (and intentionally the same wrong server url) like in the following code, I get no exception:

try {
    apolloClient.query(MyQuery())
        .watch() // flow
        .collect { response ->
            Timber.i("apollo watch collect called")
        }
} catch (e: Exception) {
    Timber.e("exception from watch execution")
    Timber.e(e)
}

Where is my error? Why don't I get an exception in the second case? How to catch errors when using watch?

3

There are 3 best solutions below

1
Shift Delete On BEST ANSWER
apolloClient.query(MyQuery())
        .watch( fetchThrows = true, refetchThrows = true) // need this
        .catch{t->}//catch here
        .collect { response ->
            Timber.i("apollo watch collect called")
        }
1
RAI On

use a callback on the ApolloQueryWatcher instance. Here is an example from GitHub

Also use .responseFetcher(ApolloResponseFetchers.CACHE_ONLY) with any enqueueAndWatch calls to make sure that watchers won't perform any network requests when the cache is updated and prevent Infinite loops

1
diziaq On

The collect function starts the flow and consumes the emitted values, but it does not throw any exceptions.

Use the onCompletion operator to handle any errors that occur during the execution of the flow.

apolloClient.query(MyQuery())
    .watch()
    .onCompletion { throwable ->
        if (throwable != null) {
            Timber.e("error from watch execution")
            Timber.e(throwable)
        }
    }
    .collect { response ->
        Timber.i("apollo watch collect called")
    }

A slight improvement: if the emitted values are being ignored it is possible to just use
.launchIn(scope) instead of .collect { response -> ...