Auto-update of apollo client cache after mutation not affecting existing queries
Asked Answered
B

1

7

I have a mutation (UploadTransaction) returning certain list of certain object named Transaction.

#import "TransactionFields.gql" 
mutation UploadTransaction($files: [Upload!]!) {
  uploadFile(files: $files){
    transactions {
      ...TransactionFields
    }
  }
}

Transaction returned from backend (graphene) has id and typename field. Hence it should automatically update Transaction in the cache. In chrome dev tools for Apollo, I can see new transactions:

enter image description here

I also have a query GetTransactions fetching all Transaction objects.

#import "TransactionFields.gql"
query GetTransactions {
  transactions {
    ...TransactionFields
  }
}

However I don't see newly added Transaction being returned by the query. During initial load, Apollo client loaded 292 transactions which it shows under ROOT_QUERY. It keeps returning same 292 transactions. UploadTransaction mutation add new object of type "Transaction" in cache in dev-tools without affecting ROOT_QUERY in dev-tools or my query in code.

enter image description here

TransactionFields.gql is

fragment TransactionFields on Transaction {
    id
    timestamp
    description
    amount
    category {
      id
      name
    }
    currency
}

Any idea what am I doing wrong? I am new to apollo client and graphql

Basidium answered 23/2, 2020 at 8:4 Comment(0)
T
21

From the docs:

If a mutation updates a single existing entity, Apollo Client can automatically update that entity's value in its cache when the mutation returns. To do so, the mutation must return the id of the modified entity, along with the values of the fields that were modified. Conveniently, mutations do this by default in Apollo Client...

If a mutation modifies multiple entities, or if it creates or deletes entities, the Apollo Client cache is not automatically updated to reflect the result of the mutation. To resolve this, your call to useMutation can include an update function.

If you have a query that returns a list of entities (for example, users) and then create or delete a user, Apollo has no way of knowing that the list should be updated to reflect your mutation. The reason for this is two fold

  • There's no way for Apollo to know what a mutation is actually doing. All it knows is what fields you are requesting and what arguments you are passing those fields. We might assume that a mutation that includes words like "insert" or "create" is inserting something on the backend but that's not a given.
  • There's no way to know that inserting, deleting or updating a user should update a particular query. Your query might be for all users with the name "Bob" -- if you create a user with the name "Susan", the query shouldn't be updated to reflect that addition. Similarly, if a mutation updates a user, the query might need to be updated to reflect the change. Whether it should or not ultimately boils down to business rules that only your server knows about.

So, in order to update the cache, you have two options:

  • Trigger a refetch of the relevant queries. You can do this by either passing a refetchQueries option to your useMutation hook, or by manually calling refetch on those queries. Since this requires one or more additional requests to your server, it's the slower and more expensive option but can be the right option when A) you don't want to inject a bunch of business logic into your client or B) the updates to the cache are complicated and extensive.
  • Provide an update function to your useMutation hook that tells Apollo how to update the cache based on the results of the mutation. This saves you from making any additional requests, but does mean you have to duplicate some business logic between your server and your client.

The example of using update from the docs:

update (cache, { data: { addTodo } }) {
  const { todos } = cache.readQuery({ query: GET_TODOS });
  cache.writeQuery({
    query: GET_TODOS,
    data: { todos: todos.concat([addTodo]) },
  });
}

Read the docs for additional details.

Turbojet answered 23/2, 2020 at 12:45 Comment(3)
Thanks Daniel. I had seen that update function is required for mutations modifying multiple entries. But missed that creation/deletion also requires it.Basidium
Is there any way you can manually update the cache for existing entities?Loyalty
"If a mutation updates a single existing entity, Apollo Client can automatically update that entity's value in its cache when the mutation returns. " -- but how does Apollo know is what is really confusing. Some of my mutations seem to update the cache, some don't, and yet they update only one entry. It's a really opaque system.Tapp

© 2022 - 2024 — McMap. All rights reserved.