Is there a way to make a query load from the cache...
# apollo-kotlin
n
Is there a way to make a query load from the cache, then perform a network request, then continue to monitor the cache for changes?
m
I don't think so. The main reason is that
CacheAndNetwork
emits 2 items so it's not included in the possible
FetchPolicy
You can use a combination of
Copy code
fetchPolicy(CacheOnly).refetchPolicy(NetworkFirst)
But that will trigger a network request on each watched item
What I'd do is use a
CacheOnly
watcher and run a
NetworkOnly
query out of the band to update the watcher if needed
n
perf, thanks!
Is there any good documentation anywhere about what refetch is?
m
I'm writing it as we speak 😅
Haven't reached the "refetch" part yet though
refetch
is what happens when a cache change has been seen by a watcher
n
heh. I don’t understand why one would set a refetch policy of network when the cache is updated. I clearly don’t understand anything about it. Don’t worry about explaining it now, i just wanna learn about it.
m
TBH, I'm not sure why would anyone use anything else than
CacheOnly
on
refetchPolicy()
It's been there forever. Let me see if I can dig some insight about why it's there
n
ok 👍
would performing a query in the
onStart
be the best spot you think?
Copy code
apollo.query(ProjectMediaNotesQuery(projectId))
            .fetchPolicy(FetchPolicy.CacheOnly)
            .watch()
            .onStart {
                apollo.query(ProjectMediaNotesQuery(projectId))
                    .fetchPolicy(FetchPolicy.NetworkOnly)
                    .execute()
            }
m
That will block your first item until the network reequest has executed so I think this is the same as a
NetworkFirst
watcher
n
ahh ok
m
I think you need to launch {} somehow, not sure you can do this 100% using suspend
Wait, why don't you do things sequentially?
Copy code
// Get from the cache
apollo.query(myQuery).fetchPolicy(CacheOnly).execute()
// Update from network and watch
apollo.query(myQuery).fetchPolicy(NetworkFirst).watch().collect {
}
n
ohhh yeahh! perfect.
m
That could be wrapped in one big
Flow
if needed:
Copy code
fun <D : Query.Data> ApolloQueryCall<D>.watchCacheAndNetwork(): Flow<ApolloResponse<D>> {
  return flow {
    try {
      emit(copy().fetchPolicy(FetchPolicy.CacheOnly).execute())
    } catch (e: ApolloException) {
    }

    copy().fetchPolicy(FetchPolicy.NetworkOnly).watch().collect { 
      emit(it)
    }
  }
}
n
heh thats nearly what I was cooking up:
Copy code
fun <T : Query.Data> Query<T>.autoUpdate(client: ApolloClient) = client.query(this)
        .fetchPolicy(FetchPolicy.NetworkFirst)
        .watch()
        .onStart {
            emit(client.query(this@autoUpdate).fetchPolicy(FetchPolicy.CacheOnly).execute())
        }
😃 1
oh but if there’s nothing in the cache it’ll throw an exception huh
m
Not in my "Flow" version above. The initial exception is ignored
n
ok
m
Because the contract is that watchers never throw
n
ahhhh gotcha, ok
m
They just live forever
Until their scope dies ...
n
👍
w
Late to the party, but
TBH, I’m not sure why would anyone use anything else than 
CacheOnly
 on 
refetchPolicy()
My understanding is that sometimes a query knows it’s outdated and should emit, but there isn’t enough data in the cache to do so. For example, a
query Items { items { id, name } }
will be observing the cache, and another query
query Other { items { id, quantity } }
is fetched from the network. If the second query contains new or different items ids, then
query Items
won’t be able to emit its data, because there are no `name`s in the cache for the new items (but it knows that
items
have changed). So with a
CacheOnly
refetch policy,
query Items
would emit a cache miss. Allowing the query to refetch from network ensures that the data in the app is consistent between different queries, even if they are disjoint like that *I’m still on Apollo 2 so something might be different in 3
👍 1
💯 1
m
That makes complete sense 👍