DEV Community

Cover image for 【Next.js, GraphQL, Apollo Client】How to rewrite Apollo Client cache after a mutation. -part2-
Kota Ito
Kota Ito

Posted on

【Next.js, GraphQL, Apollo Client】How to rewrite Apollo Client cache after a mutation. -part2-

In Part 1, I explored how to update the Apollo Client cache using RefetchQueries. However, as mentioned in the article, this approach results in unnecessary network requests. In this article, I will demonstrate how to use a GraphQL Query to read and rewrite the cache.

Using GraphQL Query

The official Apollo Client documentation presents various methods for reading and writing to the cache.

Among these, the GraphQL query is a method for both reading cache and writing new cache data.

readQuery

The readQuery method is a straightforward way to execute a GraphQL query directly on your cache. As the documentation states:

The readQuery method enables you to execute a GraphQL query directly on your cache.

In essence, readQuery allows you to retrieve data from the cache with ease.
Below is an example from the documentation on how to use readQuery:

const READ_TODO = gql` //defining the query
  query ReadTodo($id: ID!) {
    todo(id: $id) {
      id
      text
      completed
    }
  }
`;

// Fetch the cached to-do item with ID 5
const { todo } = client.readQuery({
  query: READ_TODO,
  // Provide any required variables in this object.
  // Variables of mismatched types will return `null`.
  variables: {
    id: 5,
  },
});
Enter fullscreen mode Exit fullscreen mode

writeQuery

The writeQuery is also a self-explanatory method, according to the documentation:

The writeQuery method enables you to write data to your cache in a shape that matches a GraphQL query. It resembles readQuery, except that it requires a data option.

Using writeQuery, you can update the cache to ensure that the UI remains synchronized with the data.

Below is the example use case of writeQuery.

const completeTask = async (taskId) => {
  // Optimistically update the cache
  client.writeQuery({
    query: GET_TASKS_QUERY,
    data: {
      tasks: tasks.map(task =>
        task.id === taskId ? { ...task, completed: true } : task
      ),
    },
  });

  // Send the mutation to the server
  try {
    await client.mutate({
      mutation: COMPLETE_TASK_MUTATION,
      variables: { taskId },
    });
  } catch (error) {
    // Rollback the optimistic update if the mutation fails
    client.writeQuery({
      query: GET_TASKS_QUERY,
      data: { tasks },
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

In the code example above, when a user completes a task in a to-do list, it optimistically updates the cache using writeQuery, which enables the app to update the UI accordingly. If there is an error in the process of completing the task, the cache is updated again to reflect the error state.

Update function

In my scenario, the cache needs to be modified immediately after a mutation occurs. Therefore, I provide an update function to useMutationm in which I use both readQuery and writeQuery, to manually apply changes to my cached data.

Here is the code.

const [registerPiece] = useMutation<RegisterPieceMutationData>(REGISTER_PIECE_MUTATION, {
    update: (cache, { data }) => {
      if (data) {
        const existingData: WardrobeQueryData | null = cache.readQuery({
          query: GET_WARDROBE_QUERY,
        });

        if (existingData) {
          cache.writeQuery({
            query: GET_WARDROBE_QUERY,
            data: { piece: [data.piece, ...existingData.piece] },
          });
        }
      }
    },
Enter fullscreen mode Exit fullscreen mode

As an option for REGISTER_PIECE_MUTATION, I put update function.

The update function is passed a **cache object **that represents the Apollo Client cache. This object provides access to readQuery/writeQuery that I mentioned above. Additionally, it is passed data that contains the result of the mutation, which you can use to update the cache with writeQuery method.

  if (data) {
        const existingData: WardrobeQueryData | null = cache.readQuery({
          query: GET_WARDROBE_QUERY,
        });

Enter fullscreen mode Exit fullscreen mode

In the code snippet above, if data is present, the readQuery method is triggered, and the result is stored in existingData.

if (existingData) {
          cache.writeQuery({
            query: GET_WARDROBE_QUERY,
            data: { piece: [data.piece, ...existingData.piece] },
          });
        }
Enter fullscreen mode Exit fullscreen mode

Then, if existing data is not null or undefined, it will execute writeQuery method to rewrite the cache.

The result↓
Image description

Right after the registration of the piece, a user is not required to refresh the page to see the newly registered piece now.

Conclusion

Although the example in this article demonstrated a straightforward use case for readQuery and writeQuery, updating the cache can be a bit daunting. If not done correctly, users may encounter confusing or incorrect UI results. Other than GraphQL Queries, there are also GraphQL fragments and the modify method for updating existing cache data or inserting new cache data. I will explore the GraphQL fragment next when I implement update piece detail feature.

I hope this article has been helpful for those who are just starting out with Apollo Client, like myself :)!!

Photo Credit
from:Unsplash
taken by:NASA

Top comments (0)