0
votes

This is more of a open question but hopefully it won’t get deleted.

I am using react and apollo although the question is more general.

Let’s say I have 3 distinct views in my app all using similar (but not the same) data. All of them are using separate queries but each of the query uses common operation but with slightly different data returned.

Let’s say I have a mutation somewhere that adds something to data (think of a list of items and a new item being added).

Let’s say after mutation I want to update cache to reflect that change. I am using read/writeQuery to do the update. With this setup I need to update 3 queries - this becomes a maintenance nightmare.

After some reading I figured I am doing this wrong - I have now created a single query - now I need to only update that single query after mutation and all of my views are updated automatically. However the problem is that this query now has to download all the data that all 3 views combined need - feels like this is very inefficient, because some of the views will get data they'll never use.

Is there a better way to do it?

Please note that read/writeFragment won't work because they won't update the underlying queries - check this answer for example: https://stackoverflow.com/a/50349323/2874705

Please let me know in comment if you need a more concrete example.

All in all I think in this setup I would just be better with a global state handling and avoid apollo cache all together - however I feel cheated cause apollo promised to solve the state problems :)

EDIT

Here's a concerete example:

Let's say our graphql schema is defined liked this:

type Post {
    id: ID!
    title: String!
    body: String
    published: Boolean!

}

type Query {
    posts(published: Boolean): [Post!]!
}


type Mutation {
  createDraft(body: String!, title: String!): Post
  publish(id: Int!): Post
}

Now, we create 3 queries and 2 mutations on the client

query PostTitles {
    posts {
        id
        title
    }
}

query Posts {
    posts {
        id
        title
        body
        published
    }
}

query PublishedPosts {
    posts (published: true) {
        id
        title
        body
        published
    }
}

mutation CreateDraftPost ($body: String!, $title: String!) {
    createDraft(body: $body, title: $title) {
        id
        title
        body
        published
    }
}

mutation PublishPost ($id:ID!) {
    publish (id: $id) {
        id
        published
    }
}

Just to note createDraft creates a post with the default false published value.

How can use either of those mutations to create or publish a post and have all the 3 cached queries to be updated without using refetchQueries or manualy updating each of the query? I think the real problem is that each of those queries are stored separately in the apollo in-memory cache.

2
Yes, a minimal reproducible example would help a lot. - Daniele Ricci
@DanieleRicci I have added an example - let me know if you need more info - apieceofbart

2 Answers

1
votes

From my experience, here's how it should goes.


In the case of CreateDraftPost mutation:

  1. You call the mutation and also pass an update function. In this update function, you modify the cache of the root query posts by creating a new fragment of Post and then add this fragement into posts. See this: https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
  2. Since the PostTitles and Posts all rely on the root query posts (just differ in the queried fields) and the new fragment of Post you've just added into posts has sufficient fields, your PostTitles and Posts should automatically reflect the changes.
  3. Since CreateDraftPost always create a draft with published defaults to false. You don't need to update anything related to PublishedPosts query.

In the case of PublishPost mutation:

  1. You call the mutation and the returned result is a Post with updated fields (id, published). By the mechanism of Apollo GraphQL cache, this Post (identified by id) will be updated in any queries it has involved. See this: https://www.apollographql.com/docs/react/data/mutations/#updating-a-single-existing-entity
  2. However, you need to manually update the PublishedPost query. Do this by providing update function in the mutation call. In this update function, you will readQuery of PublishedPost first, create a new Post out of the returned data and finally writeQuery to add this post into the PublishedPost results. Reference this: https://www.apollographql.com/docs/react/caching/cache-interaction/#combining-reads-and-writes

How about using refetchQueries:

  • In the case of CreateDraftPost mutation, refetch only Posts query should be sufficient (the PostTitles should be updated accordingly) since both Posts and PostTitles rely on the same root query posts and fields in Posts has also covered fields in PostTitles
  • In the case of PublishPost mutation, I would prefer refetch the PublishedPost query to avoid doing the whole update thing (since I'm lazy and I think it will not cost me much to refetch 1 query)
0
votes

It sounds like you've looked into and used the update argument that can be passed to mutation functions returned from useMutation. You're probably using proxy.readQuery and proxy.writeQuery to update it (or letting this magic happen in the background). If not, here is the documentation.

Another approach that is similar in concept but finer detail is to use proxy.readFragment and proxy.writeFragment. You can specify a set of properties on a type as being part of a fragment, and update that fragment whenever new data comes in. The nice part is that this fragment can be used within any number of queries, and if you update the fragment, those queries will update.

fragment documentations