Launch Apollo Studio

8. Update data with mutations

Learn how to update data with the useMutation hook

Time to accomplish: 12 Minutes

With Apollo Client, updating data from a graph API is as simple as calling a function. Additionally, the Apollo Client cache is smart enough to automatically update in most cases. In this section, we'll learn how to use the useMutation hook to login a user.

What is the useMutation hook?

The useMutation hook is another important building block in an Apollo app. It leverages React's Hooks API to provide a function to execute a GraphQL mutation. Additionally, it tracks the loading, completion, and error state of that mutation.

Updating data with a useMutation hook from @apollo/client is very similar to fetching data with a useQuery hook. The main difference is that the first value in the useMutation result tuple is a mutate function that actually triggers the mutation when it is called. The second value in the result tuple is a result object that contains loading and error state, as well as the return value from the mutation. Let's see an example:

Update data with useMutation

The code blocks below use TypeScript by default. You can use the dropdown menu above a code block to switch to JavaScript.

If you're using JavaScript, use .js and .jsx file extensions wherever .ts and .tsx are mentioned.

To generate TypeScript types for your queries and mutations, open another terminal window and run npm run codegen. This command watches for changes to your code and generates client-side typings.

The first step is defining our GraphQL mutation. To start, navigate to src/pages/login.tsx and copy the code below so we can start building out the login screen:

import React from 'react';
import { gql, ApolloClient, useApolloClient, useMutation } from '@apollo/client';

import { LoginForm, Loading } from '../components';
import * as LoginTypes from './__generated__/login';

export const LOGIN_USER = gql`
  mutation login($email: String!) {
    login(email: $email)

Just like before, we're using the gql function to wrap our GraphQL mutation so it can be parsed into an AST. We're also importing some components that we'll use in the next steps. Now, let's bind this mutation to our component by passing it to the useMutation hook:

export default function Login() {
  const [login, { data }] = useMutation<LoginTypes.login, LoginTypes.loginVariables>(LOGIN_USER);
  return <LoginForm login={login} />;

Our useMutation hook returns a mutate function (login) and the data object returned from the mutation that we destructure from the tuple. Finally, we pass our login function to the LoginForm component.

To create a better experience for our users, we want to persist the login between sessions. In order to do that, we need to save our login token to localStorage. Let's learn how we can use the onCompleted handler of useMutation to persist our login:

Expose Apollo Client with useApolloClient

Apollo Client ensures that your ApolloClient instance is available through React's context. Sometimes, we need to access the ApolloClient instance to directly call a method that isn't exposed by the @apollo/client helper components. The useApolloClient hook can help us access the client.

Let's call useApolloClient to get the currently configured client instance. Next, we want to pass an onCompleted callback to useMutation that will be called once the mutation is complete with its return value. This callback is where we will save the login token to localStorage.

In our onCompleted handler, we also call client.writeData to write local data to the Apollo cache indicating that the user is logged in. This is an example of a direct write that we'll explore further in the next section on local state management.

export default function Login() {
  const client: ApolloClient<any> = useApolloClient();  const [login, { loading, error }] = useMutation<LoginTypes.login, LoginTypes.loginVariables>(    LOGIN_USER,
    {      onCompleted({ login }) {        localStorage.setItem('token', login as string);        client.writeData({ data: { isLoggedIn: true } });      }    }  );

  if (loading) return <Loading />;  if (error) return <p>An error occurred</p>;
  return <LoginForm login={login} />;

Attach authorization headers to the request

We're almost done completing our login feature! Before we do, we need to attach our token to the GraphQL request's headers so our server can authorize the user. To do this, navigate to src/index.tsx where we create our ApolloClient and replace the code below for the constructor:

const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
  link: new HttpLink({
    uri: 'http://localhost:4000/graphql',
    headers: {      authorization: localStorage.getItem('token'),    },  }),

cache.writeData({  data: {    isLoggedIn: !!localStorage.getItem('token'),    cartItems: [],  },});

Specifying the headers option on HttpLink allows us to read the token from localStorage and attach it to the request's headers each time a GraphQL operation is made.

In the next section, we'll add the <Login> form to the user interface. For that, we need to learn how Apollo allows us to manage local state in our app.

Edit on GitHub