JavaScript
min read

How to use the GraphQL useMutation hook of Apollo in ReactJS

How to use the GraphQL useMutation hook of Apollo in ReactJS
Table of contents

GraphQL is a powerful query language that allows more flexibility and efficiency in data-driven applications compared to REST.

In simple words, while querying with a RESTful API, we demand extra data that is not required at that moment. Here’s where GraphQL comes in handy for requesting only the required data from the server.

Apollo is like the glue layer allowing us to use GraphQL in the front end (ReactJS in our example). We can make queries from React to our Graph of data in the server and retrieve data to present on the browser.

GraphQL


Prerequisites

  • Basic knowledge of Apollo Client library, GraphQL, and Schemas
  • Setup of GraphQL in NodeJS using express , express-graphql, and graphql
  • Setup of Apollo client and GraphQL in ReactJS using @apollo/client and graphql
  • MongoDB (mongodb and mongoose) or any other database  


Mutations and GraphiQL

Mutations allow us to mutate/change our data by performing CRUD operations. According to the official website of GraphQL, a mutation is defined as:

  • A GraphQL operation that creates, modifies, or destroys data.


We need to explicitly define mutations in the GraphQL server just like we define our RootQuery.

For example, a mutation schema definition to add an author and a book to our database would look something like this in NodeJS:


const Mutation = new GraphQLObjectType({
  name: "Mutation",
  fields: {
    addAuthor: {
      type: AuthorType,
      args: {
        name: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) },
      },
      resolve(parent, args) {
        const author = new Author({
          name: args.name,
          age: args.age,
        });
        return author.save();
      },
    },
    addBook: {
      type: BookType,
      args: {
        name: { type: new GraphQLNonNull(GraphQLString) },
        genre: { type: new GraphQLNonNull(GraphQLString) },
        authorId: { type: new GraphQLNonNull(GraphQLID) },
      },
      resolve(parent, args) {
      },
    },
  },
});

export const MyAppSchema = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation,
});


Here, the fields object contains all the mutation operations which we would like to perform. addAuthor is one such operation. AuthorType is the schema defined for the author with fields, such as ID, name, age, and books. args is the data we want to save in the database and ensure the correct datatypes.

Author is a mongoDB model, which is created using Mongoose. The resolve method is used to fetch/access, send, and mutate data in the database. The save() method is a Mongoose method that adds new author to the database. The same is the case of the addBook operation.

A mutation operation to add a book and an author sent from GraphiQL would look something like this:


mutation {
  addBook(name: "Old Path White Clouds", genre: "Biography", authorId: "646f026c0a60750fa3592095") {
    name
    genre
    authorId
  }
	addAuthor(name: "Thich Nhat Hanh", age:100){
    name
    age
  }
}


The query execution can be verified from the browser network tab with the name “graphql” (an example given at the end of this article).

GraphiQL is like a dummy frontend app that allows us to test our GraphQL server. The setup can be done from the main file or app file on our server. It is suggested specify whether we are using the production or development mode.


// setting up the graphql server
app.use(
  "/graphql",
  graphqlHTTP({
    schema: MyAppSchema, // defines schema of the graph and the object types
    graphiql: process.env.NODE_ENV === "development",
  })
);

The useMutation hook

Let’s discuss about the functionality of the useMutation() hook! It is used on the client end to send our mutation to the server.

Assuming that we have done the setup (explained later), we need to follow the steps below:

Step 0: Setting up Apollo Client in React application is done by installing @apollo/client, and creating a client object. It is a GraphQL client library using which we can make queries to the server like HTTP or Axios.


import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
import AddBook from "./components/AddBook";

// setup apollo client
const client = new ApolloClient({
  uri: "http://localhost:5000/graphql",
  cache: new InMemoryCache(),
});

function App() {
  return (
    <ApolloProvider client={client}>
      <div className="App">
        <AddBook />
      </div>
    </ApolloProvider>
  );
}

export default App;

The uri is the server endpoint to which requests are made. ApolloProvider wraps our React app and helps inject whatever data is received from the server to our frontend.


Step 1: Import gql template literal from @apollo/client library.


Step 2: Define the mutation as ADD_BOOK using gql in a separate file to ensure modularity in the project. For example, in the queries/index.js file, a mutation takes in data in the form of variables with a $ sign.


import { gql } from "@apollo/client";

export const ADD_BOOK = gql`
  mutation ($name: String!, $genre: String!, $authorId: ID!) {
    addBook(name: $name, genre: $genre, authorId: $authorId) {
      name
      id
      genre
      authorId
    }
  }
`;



Step 3: Import the useMutation()hook in the component mutation and request the above mutation to the server. This request is executed on an event of a button click to add a book. Here is where the useMutation() hook comes into picture.


import { useMutation } from "@apollo/client";
import { ADD_BOOK, GET_BOOKS } from "../queries";

const AddBook = () => {

  const [bookName, setBookName] = useState("");
  const [genre, setGenre] = useState("");
  const [authorId, setauthorId] = useState("");

  const [addBook] = useMutation(ADD_BOOK, {
    ignoreResults: false,
  });

  const submitForm = (e) => {
    e.preventDefault(); 
    addBook({
      variables: {
        name: bookName,
        authorId: authorId,
        genre: genre,
      },
      refetchQueries: [GET_BOOKS],
    });
  };

  return (
    <div>
      <form id="add-book" onSubmit={(e) => submitForm(e)}>
        {/* other form content */}
        <button>+</button>
      </form>
    </div>
  );
};

export default AddBook;

This hook takes the mutation ADD_BOOK that we just created as the first parameter and some options in an object, such as variables, and ignoreResults as the second parameter.

It also returns two elements, where the first is a mutate function (addBook), and the second is an object with details on the mutation - loading, error, and data.

It is important to make a call to this mutate function to perform the mutate operation because only calling the hook does not execute the mutation automatically (unlike GraphQL queries).

  • Variables can either be passed while calling the mutate function or while calling the useMutation hook.
  • The ignoreResults property, if set true, the mutation's data property is not updated with the mutation's result.
  • The refetchQueries property allows us the refetch certain queries once the mutation is complete.


Loading and error states are used to update the UI based on the mutation progress. They help display loading spinners or error messages while waiting for the mutation to complete or handling any encountered errors.

Finally, this is what our app and the mutation operation look like.

App & Mutation Operation using GraphQL useMutation

This is how our MongoDB database would look in the compass tool.

MongoDB in Compass


Difference between useQuery and useMutation

There are two differences between the hooks:

  • useQuery is a read-only operation used to fetch data, while useMutation is a write-only operation used to edit data and perform CRUD operations.
  • The useQuery hook runs automatically on component render, whereas the useMutation hook returns a mutate function which is needed to be triggered.


Below is an example of a query to fetch all authors and their details in GraphiQL (after we add them using the mutation specified above).

Fetch all authors in GraphiQL


Resources


Summary

The Apollo client introduces a remarkable tool: useMutation. This modern approach surpasses conventional methods. Additional libraries like apollo boost might not be required because @apollo/client comprehensive on its own. For hands on experience, Apollo offers tutorial challenges to practice with the useMutation hook before incorporating it into the project.

Notably, the graphql-http package has taken the place of express-graphql - something that can be explored. In a nutshell, the Apollo useMutation hook streamlines the process of handling GraphQL mutations, making the construction of resilient and engaging React applications simpler.

Publish date
August 25, 2023
Last update
August 25, 2023
Written by
Artwork by
No art workers.
We'd love to talk about your business objectives