apollo-feature-requests icon indicating copy to clipboard operation
apollo-feature-requests copied to clipboard

cache.modify - Expose args passed to field modified fields

Open jkdowdle opened this issue 4 years ago • 3 comments

cache.modify - Expose args passed to field modified fields

What I am hoping to accomplish with this feature request may already exist in some form and maybe I am just misunderstanding how to do it or need to rely on a field merge policy or just the exposed storeFieldName

The problem I am hoping to solve is that when an entity within the cache is updated there could be any number, sometimes even double-digit queries that need to be updated on the client. Unfourtionatley in our apps uses case, even without considering extra round trips to the server, invalidating queries will not work because sometimes the data on the backend is not updated for 5 or more seconds. Eventual consistency issues.

With Apollo Client 3.0 we now have cache.modify which seems vastly simpler than the previous ways we've tried to use readQuery and writeQuery to update all the necessary queries.

However, I was a bit surprised when I noticed that I don't have access to the variables for a query field from within cache.modify except for in a serialized form.

const [addBook] = useMutation(
    gql`
      mutation AddBook {
        addNewBook {
          id
          name
          description
          likes
        }
      }
    `,
    {
      update(client, { data }) {
        const book = data.addNewBook;
        const newBookRef = client.writeFragment({
          data: book,
          fragment: gql`
            fragment NewBook on Book {
              id
              name
              description
            }
          `,
        });
        client.modify({
          fields: {
            books(existing, { args, storeFieldName, fieldName, }) {
              args // undefined
              fieldName // "books" 
              storeFieldName //  first query - "books:{"sort":"normal"}",  second query  -"books:{"sort":"reverse"}", 
              if (storeFieldName === 'books:{"sort":"reverse"}') {
                return [newBookRef, ...existing];
              }
              if (storeFieldName === 'books:{"sort":"normal"}') {
                return [...existing, newBookRef];
              }
            },
          },
        });
      },
    }
  );

This seems to work, but I was wondering if we could just expose could add an args property there that could be constructed by deserializing the variables used for storeFeildName, or maybe there is an existing cache utility that is exported to serialize an object into the storeFieldName? Just to make sure that we stay consistent with the cache.

Another thing that seems to work, if my modify field function returns nothing, it seems like it falls back to the merge function defined in the field's type policy.

client.modify({
  fields: {
     books(existing, { args, storeFieldName, fieldName }) {},
  },
});
cache: new InMemoryCache({
  typePolicies: {
    Book: {
      keyFields: ["id"],
      fields: {},
    },

    Query: {
      fields: {
        books: {
          keyArgs: ["sort"],
          merge(existing = [], incoming, { args }) {
            args // { sort: 'reverse' | 'normal' }
            if (args.sort === "reverse") {
              return [...incoming, ...existing];
            }
            if (args.sort === "normal") {
              return [...existing, ...incoming];
            }
          },
        },
...

Am I missing something? is there a better way to orchestrate some of these cache updates? Is there a reason that args or variables couldn't be included as part of the parameters for the cache.modify field updater like it is available in the merge/read type policies?

Anyway, really appreciate the work you all have done with maintaining and working on this library! I'm really excited about the version 3 release and hope to get through upgrading our current project to be using it in production soon!

jkdowdle avatar Aug 28 '20 20:08 jkdowdle

It would be great if the args were exposed. Currently you can parse the storedFieldName as follows const args = JSON.parse(storeFieldName.replace(`${fieldName}:`, '')) but it would be much better if there was a supported way of doing this.

anark avatar Sep 19 '20 15:09 anark

Related to https://github.com/apollographql/apollo-feature-requests/issues/238

anark avatar Sep 19 '20 15:09 anark

Here's my current thinking about this idea: https://github.com/apollographql/apollo-client/issues/7129

benjamn avatar Nov 11 '20 15:11 benjamn