
How to use the GraphQL useMutation hook of Apollo in ReactJS
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.
Prerequisites
- Basic knowledge of Apollo Client library, GraphQL, and Schemas
- Setup of GraphQL in NodeJS using
express
,express-graphql
, andgraphql
- Setup of Apollo client and GraphQL in ReactJS using
@apollo/client
andgraphql
- MongoDB (
mongodb
andmongoose
) 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.
This is how our MongoDB database would look in the compass tool.
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).
Resources
- Complete source code for the example used in the blog
- Github GraphQL APIs for mutations
- Mutations in GraphQL using Apollo
- Refetching queries
- Full tutorial on GraphQL with NodeJS and ReactJS
- Mongoose documentation
- Latest module for GraphQL setup
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.