Out of curiosity, let's say that we've determined ...
# apollo-kotlin
f
Out of curiosity, let's say that we've determined that a Mutation has invalidated certain Queries (in a way that was not automatically updated by the Mutation response). Is there a way to invalidate or remove those Queries rather than re-fetch them? That way they'll be refreshed on the next natural GQL request. Sort of lazy refreshing, I guess
b
You can manually remove what’s in the cache, with
ApolloStore.remove
but it’s not per Query but per cache key, so it requires a bit of fiddling.
f
Yeah I noticed that method; is there a cache key associated with a Query or are they just for Records related to types returned by the Queries? I seem to remember the Queries being stored under a single record called QUERY_ROOT but I probably wouldn't not want to remove that whole thing, just remove certain things from it
b
No, the cache really is about fields, not queries/operations 🙂. I'm not 100% sure why the root field is named
QUERY_ROOT
but I'm guessing "Query" is probably in the same sense as the
Query
in the schema - basically all the root level fields that are available.
☝️ 1
By the way if you want to see what's currently inside your cache, you can use
NormalizedCache.prettifyDump(apolloStore.dump())
- I often find that just looking at it helps understanding/remembering how it all works
f
Yeah I seem to remember that it's not the named Query per se that matters, e.g. in some of my proof-of-concept code I ran two different named queries that both asked for the same root with the same params. They differed in fields requested under that root, but it still generated a cache hit
so I think how this works is gelling for me a bit more I'll look at the dump again but I think it must be keeping separate records of each combination of root + arguments e.g. with this Query agains the Star Wars API
Copy code
query MyQuery($first: Int = 2) {
  allFilms(first: $first) {
    films {
      id
      title
    }
  }
}
If I call this 5 times with
first
equal to 1, 2, 3, 4, 5, those will all have separate
allFilms
root entries in the cache for each And then if I call a Mutation to remove one of the movies (preferably Phantom Menace), then I'll need to either invalidate all of those entries or refresh them
nod 1
1
just thinking out loud, I'll look at the cache dump
m
Exactly! As you found out, there's nothing specific to lists in GraphQL. GraphQL doesn't know that
first
is actually the number of items in the list and that all those lists overlap
f
right, no way for it to know its meaning to our back end
m
Exactly
f
I think if there was a way for us to, instead of fetching those queries again, to simply invalidate them (so that it'll generate a cache miss on the next natural fetch of the data), that would be very useful
also if the
first
param (for example) were generated by user input (e.g. by them deciding how many films they want to fetch), it would be hard to know at the time of a subsequent Mutation which queries the user might have generated. So we'd have to probably keep a history of that so we know which ones to invalidate or fetch. I don't have an idea in mind for this, just thinking out loud
m
I think if there was a way for us to, instead of fetching those queries again, to simply invalidate them (so that it'll generate a cache miss on the next natural fetch of the data), that would be very useful
That should be possible. If you're removing a single movie (say
id = 42
, you can build its cache key programmatically and call
ApolloStore.remove(CacheKey("Movie:42"))
)
any list that contained
42
before will be invalidated
Note this requires settings up cache ids either declaratively or programmatically
f
oh ok gotcha. So even if a matching root still exists in the cache, it will try to build the response, realize that movie 42 is missing, and return a miss?
nod 1
1
m
Correct
f
Suppose that I was adding a movie. I noticed that there's a
writeFragment
so I'd probably need to make Movie a fragment then?
well I guess in that case I'd need to write the whole op
because even if the Movie exists in the db it doesn't mean the cached root references it
m
yea, adding is different cause the initial list will still be valid if you add a new item
I like to think of it like this: the list contains CacheReference (it's litterally written CacheReference in the cache):
Copy code
CacheReference("Movie:40")
CacheReference("Movie:42")
CacheReference("Movie:50")
CacheReference("Movie:7")
If you delete
"Movie:42"
then there will be a cache miss on the second item
But if you add another movie (say
"Movie:99"
) then the list still resolves
If you want to invalidate in that case, you'll have to delete the list or so
There's a low level
NormalizedCache.remove(pattern)
that you could maybe use. It's a bit hackish but it might work:
Copy code
apolloClient.apolloStore.accessCache { it.remove("allFilms%") }
f
oh interesting
ok thank you
yeah in this case the lists are still valid but if the movie were inserted in the middle of the list, maybe not
(not sure how much this matters in our codebase, I'm gaming it well beyond what I've actually tried so far)