Have `cache.modify` functions receive `options.args`
See original GitHub issueIt would be very helpful if cache.modify
received options.args
, similar to what read
and merge
functions do.
A current case that we come across, is that we have large lists of items in our cache, which are under the same field name but queried with a status argument/variable. When creating a new item, we currently query not only the resulting new item, but the lists as well. To increase speed and reduce over-fetching, we changed the mutation to only query the newly created object itself, and manually add it to several lists using cache.modify
. However, the new item should only be appended to the field values which correspond to certain arguments. To do this, we now rely on parsing of storeFieldName
strings: a much preferred solution would be to directly work with the arguments, but for that they would have to be provided through options.args
.
Since @benjamn extensively described some of the practical implications in a previous comment, I’ve included this:
@jedwards1211 I would like to see
cache.modify
functions receiveoptions.args
, somewhat likeread
andmerge
functions currently do.For
cache.modify
specifically, I’m imaginingoptions.args
would be the most recent arguments object (possiblynull
) from the last time the field’s value was updated, rather than anything more complicated, like a history of arguments or variables.Implementing
options.args
would be somewhat tricky right now (which mostly means we are unlikely to get to it before AC3 is released), because field arguments are not currently saved (or reversibly encoded) in the cache’s internal representation. However, I don’t see any immediate problems (other than slightly increasing cache size) with theEntityStore
saving the original, unfiltered arguments alongside their corresponding field values somehow, so the arguments could be recovered without any parsing ofstoreFieldName
strings.Historically, storing the original arguments alongside field values has not been necessary for reading and writing queries or fragments, because the given query/fragment plus variables allows computing specific, unambiguous arguments for any field. What you’ve sketched so far with
cache.updateFragment
in #6335 seems like it follows this unambiguous path, too, leavingcache.modify
andcache.evict
as the only two APIs that modify the cache without the guidance of a query/fragment plus variables, potentially acting on multiple field values per field name, which might have been written using different arguments.Both
modify
andevict
are new in AC3, so I’d like to give them some time to bake, so we can see how people (want to) use them. That said, I’m pretty sure theoptions.args
idea is technically feasible, without a major@apollo/client
version bump.In the meantime, please feel free to share any compelling use cases that
options.args
(or something similar) could solve.
_Originally posted by @benjamn in https://github.com/apollographql/apollo-client/pull/6289#issuecomment-634316625_
Issue Analytics
- State:
- Created 3 years ago
- Reactions:20
- Comments:16 (9 by maintainers)
Top GitHub Comments
This is an issue for far more than pagination, it impacts invalidation and modification on ROOT_QUERIES with keyArgs. The fact that the details passed to
Modifier<T>
do not include variables, other than what is serialized in thestoreFieldName
makes modifying queries which share the same field name but have a large number of variables extremely difficult, the same is true with cache.evict. I think exposing stored keyArgs as part ofdetails
in theModifier<T>
function would be very helpful, as it is already is stored in the storeFieldName as a string. Unfortunately, to get those args into a JS object requires ugly hacks to parse the cacheKey to a javascript object.For example, imagine a query which has 4-5 optional variables which are all used a keyargs. There may be hundred or more in the cache (think typeahead), with a huge number of potential combinations. If you add an item via a mutation, there is no easy way to search through the cache to determine which queries you need to modify because you have NO access to the keyargs. You literally would have to guess every possible combination and check if it exists in the cache.modify function.
I appologize for any errors in the code below. As I am typing it directly here.
Assume all of these variables are used as keyArgs. Every time a user updates keyword, it will create a new cache entry, or if they change the type, etc. Assume I add a new item of type “Foo” to campaign id “1”. That that means I need to figure out which GetItems queries have a
campaignId
of “1” or a type offoo
and modify them.For example this update function using cache.modify, doesn’t really leave me a great way to figure out which of the getItems on the root query I really care about.
What would be better
This also extends to
cache.evict
.I use this hacky helper function frequently, to determine which queries I need to evict.
invalidateApolloCacheFor
takes two arguments. The first, is the Apollo cache. The second is a function which receives the field and key args of every ROOT_QUERY item in the cache. If the filter returns true, the helper will evict from the cache.The main “trick” here is the helper parses the cache key (which has the key args included) into an object containing the field name and key args. The filter function is then invoked with this new object. This helper could be removed if there was a way to get at the keyArgs for each field in the cache other than parsing the storeFieldName string.
Hey I had the same issue some time ago but implemented a solution which has helped me so far and seems to work well, so I’ll post it here if it helps anyone. Note: the one caveat is that I need to be able to get my input args wherever I use this but that hasn’t been an issue for me
I just hash the keyArgs in my type policies when I need it on specific fields
Then in my cache modify whenever I need to use it I just rebuild the key and this allows me to get the data I need or perform operations specific to that dataset and return the structure accordingly
We use namespaces (ie. user) but this should be enough for you to repurpose for your use cases. Hope this helps as I remember struggling on this for a lonnng time a year or so ago