Mutations
Learn how to update data with the useMutation hook
Now that we've learned how to fetch data from our backend with Apollo Client,
the natural next step is to learn how to update that data with mutations.
This article demonstrates how to send updates to your GraphQL server with the
useMutation
hook. You'll also learn how to update the Apollo Client cache
after executing a mutation, and how to track loading and error states for a mutation.
Prerequisites
This article assumes you're familiar with building basic GraphQL mutations. If you need a refresher, we recommend that you read this guide.
This article also assumes that you've already set up Apollo Client and have wrapped your React app in an ApolloProvider
component. Read our getting started guide if you need help with either of those steps.
To follow along with the examples below, open up our starter project and sample GraphQL server on CodeSandbox. You can view the completed version of the app here.
Executing a mutation
The useMutation
React hook is the primary API for executing mutations in an Apollo application. To run a mutation, you first call useMutation
within a React component and pass it a GraphQL string that represents the mutation. When your component renders, useMutation
returns a tuple that includes:
A mutate function that you can call at any time to execute the mutation
An object with fields that represent the current status of the mutation's execution
Let's look at an example. First, we'll create a GraphQL mutation named ADD_TODO
, which represents adding an item to a to-do list. Remember to wrap GraphQL strings in the gql
function to parse them into query documents:
1import gql from 'graphql-tag';
2import { useMutation } from '@apollo/react-hooks';
3
4const ADD_TODO = gql`
5 mutation AddTodo($type: String!) {
6 addTodo(type: $type) {
7 id
8 type
9 }
10 }
11`;
12
1import gql from 'graphql-tag';
2import { Mutation } from '@apollo/react-components';
3
4const ADD_TODO = gql`
5 mutation AddTodo($type: String!) {
6 addTodo(type: $type) {
7 id
8 type
9 }
10 }
11`;
Next, we'll create a component named AddTodo
that represents the submission form for the to-do list. Inside it, we'll pass our
ADD_TODO
mutation to the useMutation
hook:
1function AddTodo() {
2 let input;
3 const [addTodo, { data }] = useMutation(ADD_TODO);
4
5 return (
6 <div>
7 <form
8 onSubmit={e => {
9 e.preventDefault();
10 addTodo({ variables: { type: input.value } });
11 input.value = '';
12 }}
13 >
14 <input
15 ref={node => {
16 input = node;
17 }}
18 />
19 <button type="submit">Add Todo</button>
20 </form>
21 </div>
22 );
23}
1const AddTodo = () => {
2 let input;
3
4 return (
5 <Mutation mutation={ADD_TODO}>
6 {(addTodo, { data }) => (
7 <div>
8 <form
9 onSubmit={e => {
10 e.preventDefault();
11 addTodo({ variables: { type: input.value } });
12 input.value = '';
13 }}
14 >
15 <input
16 ref={node => {
17 input = node;
18 }}
19 />
20 <button type="submit">Add Todo</button>
21 </form>
22 </div>
23 )}
24 </Mutation>
25 );
26};
Calling the mutate function
The useMutation
hook does not automatically execute the mutation you
pass it when the component renders. Instead, it returns a tuple with a mutate function in its first position (which we assign to addTodo
in the example above). You then call the mutate function
at any time to instruct Apollo Client to execute the mutation. In the example above, we call addTodo
when the user submits the form.
Providing options
Both useMutation
itself and the mutate function accept options that are described in the API reference. Any options you provide to a mutate function override corresponding options
you previously provided to useMutation
. In the example above, we provide the
variables
option to addTodo
, which enables us to specify any GraphQL variables that the mutation requires.
Tracking mutation status
In addition to a mutate function, the useMutation
hook returns an object that
represents the current state of the mutation's execution. The fields of this
object (fully documented in the API reference) include booleans that indicate whether the mutate function has been called
yet, and whether the mutation's result is currently loading
.
Updating the cache after a mutation
When you execute a mutation, you modify back-end data. If that data is also present in your Apollo Client cache, you might need to update your cache to reflect the result of the mutation. This depends on whether the mutation updates a single existing entity.
Updating a single existing entity
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.
Let's look at an example that enables us to modify the value of any existing item in our to-do list:
1const UPDATE_TODO = gql`
2 mutation UpdateTodo($id: String!, $type: String!) {
3 updateTodo(id: $id, type: $type) {
4 id
5 type
6 }
7 }
8`;
9
10function Todos() {
11 const { loading, error, data } = useQuery(GET_TODOS);
12 const [updateTodo] = useMutation(UPDATE_TODO);
13
14 if (loading) return <p>Loading...</p>;
15 if (error) return <p>Error :(</p>;
16
17 return data.todos.map(({ id, type }) => {
18 let input;
19
20 return (
21 <div key={id}>
22 <p>{type}</p>
23 <form
24 onSubmit={e => {
25 e.preventDefault();
26 updateTodo({ variables: { id, type: input.value } });
27
28 input.value = '';
29 }}
30 >
31 <input
32 ref={node => {
33 input = node;
34 }}
35 />
36 <button type="submit">Update Todo</button>
37 </form>
38 </div>
39 );
40 });
41}
1const UPDATE_TODO = gql`
2 mutation UpdateTodo($id: String!, $type: String!) {
3 updateTodo(id: $id, type: $type) {
4 id
5 type
6 }
7 }
8`;
9
10const Todos = () => (
11 <Query query={GET_TODOS}>
12 {({ loading, error, data }) => {
13 if (loading) return <p>Loading...</p>;
14 if (error) return <p>Error :(</p>;
15
16 return data.todos.map(({ id, type }) => {
17 let input;
18
19 return (
20 <Mutation mutation={UPDATE_TODO} key={id}>
21 {updateTodo => (
22 <div>
23 <p>{type}</p>
24 <form
25 onSubmit={e