apollographql / apollo-kotlin

:rocket:  A strongly-typed, caching GraphQL client for the JVM, Android, and Kotlin multiplatform.
https://www.apollographql.com/docs/kotlin
MIT License
3.73k stars 654 forks source link

How to prevent arrays inside cached object being overwritten? #6023

Open isaacmanu opened 2 months ago

isaacmanu commented 2 months ago

Question

Version 4.0.0-beta.4

Hi,

In our schema we have an object like this:

type SimpleUser implements User {

  id: ID!

  books: [Book]

  name: String!

  someObjects: [SomeObject]
}

And we have a CacheKeyGenerator like so:

class ApolloCacheKeyGenerator : CacheKeyGenerator {
    override fun cacheKeyForObject(obj: Map<String, Any?>, context: CacheKeyGeneratorContext): CacheKey? {
        return when (val typename = obj.getOrDefault("__typename", "")) {
            "SimpleUser" -> CacheKey("$typename:${obj["id"]}")
            else -> null
        }
    }
}

We can receive this SimpleUser object from multiple different queries, and each time we receive it, it can contain a different array of Book and SomeObject, which results in the newest received SimpleUser overwriting the previous one. Is there an easy way for me to merge the new and old arrays every time we receive a SimpleUser regardless of which query it comes from?

Thank you!

BoD commented 2 months ago

Hi!

One way is to use the ApolloStore API to manually store a SimpleUser that is a combination of the one existing in the cache, and the one you just received from the network. You'd have to do that at all the places that execute queries that return SimpleUser.

There are also incubating (experimental) APIs that allow to pass the desired merge algorithm, which will be called whenever an object needs to be merged. These exist to help with pagination scenarios but If you are willing to try experimental APIs I believe they could also be used for your use-case.

Both approaches are described in this document. Hope this helps!

isaacmanu commented 1 month ago

Thank you for your reply! And sorry for the slow follow up.

I'm trying to implement the second approach you listed above. However I'm running into this issue

Duplicate class com.apollographql.apollo3.cache.normalized.ApolloStore found in modules apollo-normalized-cache-incubating-jvm-4.0.0-beta.4.jar -> jetified-apollo-normalized-cache-incubating-jvm-4.0.0-beta.4 (com.apollographql.apollo3:apollo-normalized-cache-incubating-jvm:4.0.0-beta.4) and apollo-normalized-cache-jvm-4.0.0-beta.4.jar -> jetified-apollo-normalized-cache-jvm-4.0.0-beta.4 (com.apollographql.apollo3:apollo-normalized-cache-jvm:4.0.0-beta.4)

When attempting to import

implementation("com.apollographql.apollo3:apollo-runtime:4.0.0-beta.4")
implementation("com.apollographql.apollo3:apollo-normalized-cache-incubating:4.0.0-beta.4")

Any suggestions on how I could resolve this? For clarity's sake this is the only place we declare dependencies on Apollo.

BoD commented 1 month ago

Hmm it looks like your project depends on both apollo-normalized-cache and apollo-normalized-cache-incubating. Can you maybe try ./gradlew :app:dependencies to understand why?

By the way, head up that since the RC1 release of Apollo Kotlin 4, we've changed the group ids, and moved the incubating library outside of the main repository (a bit of context about this here) - so your dependencies should now be:

implementation("com.apollographql.apollo:apollo-runtime:4.0.0-rc.1")
implementation("com.apollographql.cache:apollo-normalized-cache-incubating:0.0.2")

Don't hesitate to let us know how that goes.