Labs ICT
Pro Login

Client-Side Mutations

Client-Side Mutations

Mutations on the client side let you modify data on the server and update your application's state. Apollo Client makes this straightforward with the useMutation hook.

Basic Mutations

The useMutation hook returns a function that triggers the mutation and the result state:

import { useMutation, gql } from '@apollo/client';

const CREATE_USER = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

function CreateUserForm() {
  const [createUser, { loading, error, data }] = useMutation(CREATE_USER);

  const handleSubmit = async (e) => {
    e.preventDefault();
    await createUser({
      variables: {
        input: {
          name: 'Charlie',
          email: 'charlie@example.com',
        },
      },
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      {loading && <p>Creating user...</p>}
      {error && <p>Error: {error.message}</p>}
      {data && <p>User created: {data.createUser.name}</p>}
      <button type="submit">Create User</button>
    </form>
  );
}

The mutation function is lazy - it only executes when you call it. The result contains loading, error, and data states.

Updating the Cache

After a mutation, you often need to update the cache so your UI reflects the changes. Apollo Client provides the update function for this:

const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
    }
  }
`;

const CREATE_USER = gql`
  mutation CreateUser($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`;

function CreateUserForm() {
  const [createUser] = useMutation(CREATE_USER, {
    update(cache, { data: { createUser } }) {
      // Read the current cache
      const { users } = cache.readQuery({ query: GET_USERS });

      // Write the new user to the cache
      cache.writeQuery({
        query: GET_USERS,
        data: { users: [...users, createUser] },
      });
    },
  });
}

The update function lets you manually update the cache after a mutation. This keeps your UI in sync with the server.

Refetching Queries

Instead of manually updating the cache, you can refetch queries after a mutation:

function CreateUserForm() {
  const [createUser] = useMutation(CREATE_USER, {
    refetchQueries: [{ query: GET_USERS }],
  });

  // Or just refetch specific queries
  const [deleteUser] = useMutation(DELETE_USER, {
    refetchQueries: ['GetUsers'], // Can use query names
  });
}

refetchQueries is simpler but makes an extra network request. Use it when the cache update logic is complex.

Optimistic Updates

Optimistic updates make your UI feel faster by immediately updating the cache before the server responds:

const [createUser] = useMutation(CREATE_USER, {
  optimisticResponse: {
    createUser: {
      __typename: 'User',
      id: 'temp-id',
      name: 'Charlie',
      email: 'charlie@example.com',
    },
  },
  update(cache, { data: { createUser } }) {
    const { users } = cache.readQuery({ query: GET_USERS });
    cache.writeQuery({
      query: GET_USERS,
      data: { users: [...users, createUser] },
    });
  },
});

If the mutation fails, Apollo automatically rolls back the optimistic update. This gives users instant feedback while maintaining data integrity.