How to Consume GraphQL API with Apollo Client in a Reactjs Application
Tags: graphql, react, frontend, javascript
Published: 2023-04-04 13:41:48

Now that we understand what GraphQL is and why some companies prefer it over the RESTful API, let's talk about consuming GraphQL endpoints in your Reactjs application.

> If you haven't read Part 1 of this blog post, you can [do so here (click)](https://dev.to/stephengade/what-is-graphql-understanding-graphql-as-a-frontend-developer-1m0a)

## GraphQL Operations

You need one GraphQL endpoint for your entire application to perform different operations such as:

### Mutation: 

Sending data you collected from the UI to the database.


### Query: 

Fetching data to UI

We also have other operations like `Subscription`, but you'll be using Mutation and Query a lot.

Before we start working with data, let's set up our project and configure it.

Although there are many ways to work with a GraphQL API in Reactjs, such as using the Fetch method, React query, or Axios, we are not going to use any of these but rather, we'll be using Apollo Client from Apollo GraphQL.

## What is Apollo Client?

Apollo Client lets you manage the state of your data with GraphQL. It allows you to consume GraphQL endpoints without using any external state management.
 

> **Hint**: If you've worked with React Query before, you probably have an idea of using Apollo Client to consume GraphQL endpoints because they have the same syntax and solving the same problem.

**_Now, let's start!_**

Run `npx create next-app app-name` to bootstrap a React project with Nextjs.

Clean it up, navigate to the root level of your project, and create a new file called `Apollo-client.js`

Now install Apollo Client and "Js-cookies" packages:

`yarn add @apollo/client js-cookie`

This command will install some dependencies into our project, and we'll use the `js-cookie library` to store and retrieve data in our browser's storage.

In `Apollo-client.js`, put this code to import everything we need to setup Apollo Client:

```javascript
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
import { onError } from "@apollo/client/link/error";
import Cookies from 'js-cookie'

import { setContext } from "@apollo/client/link/context";
```

These will let us handle errors, cache data, refetch, manage the local and remote state of our operations, and retrieve cookies.

Let's set the header token. The idea is that when a user logs in to your application, they'll be given an access token, and we are now going to set the access token to the header.

> Note: This is compulsory for most projects especially if it's private as only authenticated people will be given access to interact with the APIs.

So update your `apollo-client.js` file with:

```javascript
const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const authToken  = Cookies.get("secret__");
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: `Bearer ${authToken}`
    }
  }
});
```

Leave apollo-client.js file for now, create another file called `env.local` which is used to store private keys and values.

In `env.local` file, let's store our GraphQL endpoint:

NEXT_PUBLIC_API_URL=https://your-graphql-api.com

Now go back to apollo-client and add this code:

```javaacript
const httpLink = new HttpLink({
  uri: `${process.env.NEXT_PUBLIC_API_URL}/graphql`,
  credentials: "include",
});
```

Let's handle any potential error, in the `apollo-client.js` file, add:

```javascript
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
      ),
    );

  if (networkError) console.log(`[Network error]: ${networkError}`);
});
```

Now, we need to make this apollo-client.js accessible every time we need it. In the same apollo-client.js file, add:

```javascript
const client = new ApolloClient({
    cache: new InMemoryCache(),
    connectToDevTools: true,
     link: authLink.concat(httpLink),
});

// Export it

export default client;
```

We are done setting up the apollo-client file. But do you notice this line in the client:

`connectToDevTools: true`

This allows you to explore your GraphQL API in your browser powered by Apollo Client. If you leave the value as true, you'll see a new Tab called `Apollo` added to your tab when you inspect the page.

The full code for the `apollo-client.js` is this:

```javascript
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
import { onError } from "@apollo/client/link/error";
import Cookies from 'js-cookie'

import { setContext } from "@apollo/client/link/context";

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const authToken  = Cookies.get("secret__");
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: `Bearer ${authToken}`
    }
  }
});

const httpLink = new HttpLink({
  uri: `${process.env.NEXT_PUBLIC_API_URL}/graphql`,
  credentials: "include",
});

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
      ),
    );

  if (networkError) console.log(`[Network error]: ${networkError}`);
});

const client = new ApolloClient({
    cache: new InMemoryCache(),
    connectToDevTools: true,
     link: authLink.concat(httpLink),
});

// Export it

export default client;
```

If we are on the same page, we can now start working with GraphQL endpoints.

Let's start with `Mutation`

## How to: Mutation in GraphQL

**Mutation** simply means to change or alter.

When you're collecting data from user and sending it to the backend, something is changing.

For example, the data of such a user is changing from an empty state to a filled-up state.

So generally, GraphQL mutation operations on frontend mean you're sending in data. And let's demonstrate this by building a simple `Register` component.

Create a new file inside the 'component' file and call it `RegisterUser.jsx` and put your registration form.

```javascript
// RegisterUser.jsx

const RegisterUser = () => {

const [registerData, setRegisterData] = useState({
 name: " ",
 age: " ",
 password: " "
})

// handle form change

const handleChange = (e) => {
 setRegisterData({...registerData, [e.target.name]: e.target.value})
}

// Handle submit function

const handleRegister = (e) => {
 e.preventDefault()

try {
 if(registerData) {
  console.log(registerData)
}
} catch(e) {
 console.log(e)
}
}
  

return (
  <section>
   
  <form on submit={handleRegister}>
     <input name="name" value={registerData.name} />

<input name="age" value={registerData.age} onChange={handleChange} />

<input name="password" value={registerData.password}
onChange={handleChange}
 />

<button type="submit" >Register</button>

  </form>

</section>
)
}
```

This is a simple form, you can style it yourself. The next thing is to submit the form to Backend.

We don't have an endpoint for this, let's assume we have one, here is what we need to do:

### Create the mutation

Import the `useMutation` hook provided by Apollo Client 
Pass the user data as the variable to the mutation.


> **HINT**: Writing the query or mutation syntax can be time-consuming because you have to ensure everything is correct. But no worries, GraphQL is introspective, use this website to see and write your queries and mutations: `https://studio.apollographql.com/`, just put your GraphQL API and it will show you everything on that API.

You can as well use the playground by clicking on putting your API link in the browser to see something like this:


![GraphQL API Playground](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75cggafcd04ln93ltcw7.png)



So, you have 3 options to introspect your endpoint: Apollo DevTools, Apollo Studio or the default playground.

Let's continue.

### Create the mutation

To create the mutation, import gql.

In the register file:

```javascript

import {gql} from @apollo/client;

// Create the mutation

const REGISTER_MUTATION = gql` 
mutation Register($name: String!, $password: String!, $age: String!) { registerUser(registerInput: { name: $email, password: $password, age: $age}) {
 _id 
age
name 
} } `;

```

Before we move on, let me explain the mutation above. 


We store the mutation as `“REGISTER_MUTATION”` which you can use anything. Then, we use the `gql` exported from apollo-client to serialize the mutation to javascript understandable code format.

Then this line: `mutation Register($name: String!, $password: String!, $age: String!)`

This is called the variables: `($name: String!, $password: String!, $age: String!)`, that is, the expected data to be sent to the backend. The exclamatory sign `”!”` shows that the data is required, if you don’t send it to the backend, you’ll see `“BAD REQUEST”` error.

And finally, we return some data if the operation is successful:

```javascript
{
 _id 
age
name 
}
```

It is optional to return these data.

Now, let’s continue.

1. Import the `useMutation` hook from the Apollo client which we will use to run this operation.
2. Initiate the mutation using the hook.
3. and finally, create the `handleSubmit` function that will send this data to the backend.
 
```javascript

Import {useMutation} from “@apollo/client”


// Initiate the mutation like this:

const [RegisterUser, {data, error, loading}] = useMutation(REGISTER_MUTATION, {
 variables: {
  // pass the form state here as values to the variables
age: registerData.age
name: registerData.name
Password: registerData.password

}
});

// handle Form submit

const handleSubmit =  async () => {
  // call the mutation when submit button is clicked in a try-catch block

 try {
await RegisterUser()

} catch(error) {
console.log(error)
}

 }
```

Note this line: `{data, error, loading}`, this the default destructured props given to you by Apollo client to track the progress of your request.

- `data` - The data returned by the operation
- `error` - If there is error when running the operation
- `loading` - If the request is still processing.

You can use these properties in your component, for instance, you can disable the submit button if the request is still loading and display error if it fails.

Finally, go to form submit button and invoke the function there:

```javascript
<button type="button"  onClick={handleSubmit} disabled={loading}>Register</button>
```

That’s it and you’re done.

The full code now looks like this:

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

const REGISTER_MUTATION = gql` 
mutation Register($name: String!, $password: String!, $age: String!) { registerUser(registerInput: { name: $email, password: $password, age: $age}) {
 _id 
age
name 
} } `;


export const RegisterUser = () => {

const [registerData, setRegisterData] = useState({
 name: " ",
 age: " ",
 password: " "
})

// handle form change

const handleChange = (e) => {
 setRegisterData({...registerData, [e.target.name]: e.target.value})
}

// Initiate the mutation like this:

const [RegisterUser, {data, error, loading}] = useMutation(REGISTER_MUTATION, {
 variables: {
  // pass the form state here as values to the variables
age: registerData.age
name: registerData.name
Password: registerData.password

}
});

// handle Form submit

const handleSubmit =  async () => {
  // call the mutation when submit button is clicked in a try-catch block

 try {
await RegisterUser()

} catch(error) {
console.log(error)
}

 }

  

return (
  <section>

{/*If  there is an error, return the error message */}


{error && <p className=”error”>{error.message}</p>
   
  <form>
     <input name="name" value={registerData.name} />

<input name="age" value={registerData.age} onChange={handleChange} />

<input name="password" value={registerData.password}
onChange={handleChange}
 />

<button type="button"  onClick={handleSubmit}>Register</button>



  </form>

</section>
)
}

```


Now, let’s talk about Query:

## How to: Query in GraphQL

Whenever you want to fetch data from the backend, you use Query and you use a hook from apolloClient called `useQuery`.

Just like the mutation we did above, the same syntax will be used here. But it is worth noting that some Queries might require variables while some don’t.

To demonstrate this, I will use a sample Query markup.

Assuming you want to fetch all the users who register on your website, the query will look like this:


```graphql

{
AllUsers {
// Return the data you need

age,
name
}
}
```

In the query above, we did not pass any variable, we just fetch directly.

A scenario where you can use a variable in Query is when you are fetching data for a specific user. For instance, let’s say you want to fetch a user’s data with his `_id`, the query will look like something like this:

```graphql
   user(_id: “12”} {
  age
  name
}
```

Here we are passing the variable to it directly, so we will see a user with an id of 12.

As long as these two samples are valid, it is not a good practice to run a query like that as a front-end developer. Imagine how we hard-code the variable, it has to be dynamic.

So let’s use a good sample, let’s assume we want to fetch all users and a specific user, the full code is as follow:

```javascript
Import {useQuery, gql} from “@apollo/client”

// store the query here

Const FETCH_ALL_USERS = gql`
  AppUsers {
  AllUsers {
  age
 name
}
}
`

const AllUsers = () => {
  
const {data, error, loading} = useQuery(FETCH_ALL_USERS) // we don’t need to pass any variables

 return (
  <section>
 <h2>name : {data.AllUsers.name} </h2>
 <h2>age : {data.AllUsers.age} </h2>

</section>

)

```

And we are done. 

Note, don’t just return data by saying `data.name` in this scenario. Check the data structure of the query before you return.

In a case where you need to pass a variable:

```javascript
Import {useQuery, gql} from “@apollo/client”

// store the query here

Const FETCH_USER = gql`
  SingleUser(_id: string!) {
  User(_id: $_id) {
  age
 name
}
}
`

const User = () => {
  
const {data, error, loading} = useQuery(FETCH_USER, {
 // pass the id as variable

 variables: {
_id: USER_ID_HERE
}
})

 return (
  <section>
 <h2>name : {data.User.name} </h2>
 <h2>age : {data.User.age} </h2>

</section>

)

```

And we are good.

In the previous series, I said we can track the state of our operation which is one of the benefits of using GraphQL over RESTful API and we have seen how to we use the `data`, `loading`, and `error` properties, but Apollo client provides you with more such as:

- `onCompleted`: Do something if the operation is successful
- `onError`: Do something if there’s an error

And many more, here is how to use it like the `variables`:

```graphql
 
const {data, error, loading} = useQuery(FETCH_USER, {
 // pass the id as variable

 variables: {
_id: USER_ID_HERE
},

onCompleted() {
  // trigger something like a success modal or snackbar here
},

onError() {
// trigger something like an error modal or snackbar here}
})

```

So that’s it! To learn more about GraphQL, I will advise you [check Apollo GraphQL documentation here](https://www.apollographql.com/), they have the best documentation you can trust.

Thanks for reading and don’t forget to ask questions, share this with something and follow me 🙏