How to use React hooks in Apollo Client for GraphQL
Tags: litmuschaos, react, graphql, typescript
Published: 2020-09-22 07:33:03

Hello World! In this blog, I am going to discuss how we can use React hooks with Apollo to connect to GraphQL API in different scenarios. Assuming that you have a basic understanding of the same, I will be explaining how GraphQL data can be shared with the UI using React hooks by giving a few examples that we are already using in our ongoing project, the [Litmus Portal](https://github.com/litmuschaos/litmus/tree/master/litmus-portal). 

![GraphQL](https://dev-to-uploads.s3.amazonaws.com/i/xl08k5h7vtpasoco0klh.png)

#**What is GraphQL?**

Before going forward, let me give a very brief overview of GraphQL and what all things we are going to discuss. So GraphQL is a query language for APIs that is developed by Facebook. It is an efficient alternative to REST because of its features such as:
* With GraphQL there's no over fetching or under fetching of data, unlike REST.
* Strongly typed graphQL schema which can be written in GraphQL Schema Definition Language (SDL) helps you validate your API requests during its compile time.
* With the development of various GraphQL libraries (Apollo, Relay, etc) you are getting a lot of features such as caching, realtime data, etc.
* It provides a large and amazing community! You can always get your queries answered whenever stuck. 

This was just a basic introduction to GraphQL, but I recommend you to visit the site to gain deeper insights of the same. 

#**What we'll do?**

I will be mainly focussing on the front-end side, where I am gonna explain the two very useful react hooks `useQuery` and `useMutation`, how are we using these in our project for GraphQL operations along with the code.

##**Wait...what are GraphQL operations?**##

GraphQL provides various types of operations such as Query, Mutation, and Subscription which act as the entry points for the requests sent by the client. In this blog, I'll be discussing the first two types i.e. Query and Mutation.

* **Query:** `useQuery` hook is used to fetch the data from the server and attach it to the UI. To run a query you need to call this hook by passing the query string, it returns an object from Apollo client containing `data`, `error`, `loading` properties which change their values after execution. The hook is called when the component renders and the above properties can be used conditionally to render the UI. 

**Basic syntax:**

```
const { loading, error, data } = useQuery<Type1, Type2>(
    QUERY_STRING,
    { variables: <variable>,
onCompleted:()=>{console.log("query successful",data);}
,
onError:(error)=>{console.error(error);},
});
```
1. `data` : The required data we are getting after the query is successful.
2. `loading` : It is a boolean value, if `true`, it means the query is still in flight. After it is successful the value of `loading` changes to `false`.
3. `error` : It stores the error if occurred while querying.

* **Mutation:** `useMutation` hook is used to send updates to the GraphQL server as a result of which data can be updated in the back-end. It is somewhat similar to `useQuery` in terms of syntax with some minor differences. To execute a mutation, you need to pass the mutation string to the hook. This hook returns a tuple containing a `mutate` function which can be called whenever its execution is required and an object having certain fields that represent the current status of mutation's execution.

**Basic syntax:**

```
 const [mutateFunction,{ error,loading}] = useMutation<Type>(MUTATION_STRING, {
    onCompleted: () => {
    console.log("details updated")
    },
    onError: (error) => {
    onError:(error)=>console.error(error);
    },
    refetchQueries: [{ query: QUERY_STRING, variables: <variable>],
  });
```

1. `mutateFunction` : It is the `mutate` function which can be called anytime to run the mutation.
2. The second parameter is the object representing the mutation's execution status such as `error` , `loading` which have been explained above.

In both examples, I have added options to the hooks:
1. `onCompleted` : It is a callback executed after a successful query/mutation.
2. `onError` : Callback executed in case of an occurrence of error.
3. `refetchQueries` : It takes an array or function which is used to specify a list of queries that need to be refetched after the mutation is successful.

![best practices](https://dev-to-uploads.s3.amazonaws.com/i/ijrglfq8825mgt68nnoa.png)

###**Some of the good practices you can follow:**###

* Type the data you are sending or receiving during the requests wherever it is required. It enhances readability and understandability.
* As a beginner we often tend to store the data we received from the requests in local states which is not required. Apollo Client provides an in-memory cache in which it stores the data that helps the client to respond to future queries for the same data without sending unnecessary requests. So instead of storing it in local states we can directly access and use it without making repeated requests.

Now I'll be explaining some examples that we have used in our ongoing project, the Litmus Portal.

![Litmus](https://dev-to-uploads.s3.amazonaws.com/i/bw3hdpvknhsn6y48r3po.png)

#**LitmusChaos**
[LitmusChaos](https://litmuschaos.io/) is an open-source toolset to practice chaos engineering in cloud-native systems. It comes up with a large set of chaos experiments that are hosted on the [hub](https://hub.litmuschaos.io). For further details, you can check out our [github](https://github.com/litmuschaos/litmus) repo. Litmus Portal provides a console and UI experience for managing, monitoring, and events around chaos workflows. It is being developed using React and TypeScript for the front-end and Golang for the back-end. 

#**Examples**
Without any further delay, let's get started!!

![coding cat](https://media.giphy.com/media/LmNwrBhejkK9EFP504/giphy.gif)


> Since the examples I am going to explain are a part of a project, I have excluded some parts of the logic which are not relevant to the blog. You can find the complete code [here](https://github.com/litmuschaos/litmus/tree/master/litmus-portal).

###**Query**###

**Schema**
```
export const GET_USER = gql`
  query getUser($username: String!) {
    getUser(username: $username) {
      username
      email
      id
      name
      projects {
        members {
          user_id
          user_name
          role
          invitation
          name
          email
          joined_at
        }
        name
        id
      }
      company_name
      updated_at
      created_at
      removed_at
      is_email_verified
      state
      role
    }
  }
`;

export const ALL_USERS = gql`
  query allUsers {
    users {
      id
      name
      username
      email
    }
  }
`;
```
The `GET_USER` query string returns the complete details of a user whose `username` is passed as a variable.
The `ALL_USERS` query string returns a list of all users who are present along with their details including `id`, `name`, `username`, and `email`.

**useQuery**

```
const { data: dataB } = useQuery<CurrentUserDetails, CurrentUserDedtailsVars>(
    GET_USER,
    { variables: { username: userData.username } }
  );

 const { data: dataA } = useQuery(ALL_USERS, {
    skip: !dataB,
    onCompleted: () => {
    
    //consoles the list of all users present
    console.log(dataA.users);
    },
    onError: (error) => {
    //in case of error, it prints the error message in the console
    console.error(error.message)
  });
```

In the above example, I have two queries:
* `GET_USER` : I am sending the `username` as variable to get all the details associated with that username. The received data can be accessed through `dataB`. `CurrentUserDedtailsVars` is the type of data I am sending i.e. the username and `CurrentUserDetails` is the type of data I am receiving on a successful query. These types are stored in a separate file:

```
export interface Member {
  user_id: string;
  user_name: string;
  role: string;
  invitation: string;
  name: string;
  email: string;
  joined_at: string;
}

export interface Project {
  members: Member[];
  name: string;
  id: string;
}

export interface UserDetails {
  username: string;
  projects: Project[];
  name: string;
  email: string;
  id: string;
  company_name: string;
  updated_at: string;
  created_at: string;
  removed_at: string;
  is_email_verified: string;
  state: string;
  role: string;
}

export interface CurrentUserDetails {
  getUser: UserDetails;
}

export interface CurrentUserDedtailsVars {
  username: string;
}
```

* `ALL_USERS` : This query is for fetching the list of all the users which can be accessed through `dataA`.

`skip` : This is a boolean value, if true, the query will be skipped. In the above logic if `dataB` is empty i.e. unless and until `GET_USER` query is successful `ALL_USERS` query will be skipped. Once `dataA` gets populated then the second query is executed. This option is useful in the cases where you need to execute the queries in specific order.
 
###**Mutation**###

**Schema**
```
export const SEND_INVITE = gql`
  mutation sendInvite($member: MemberInput!) {
    sendInvitation(member: $member) {
      user_id
      user_name
      role
      invitation
    }
  }
`;
```
The `SEND_INVITE` mutation string is used to send an invitation to a user for a selected project. Once the user accepts the invitation he/she becomes a member of that project too. As `MemberInput` we need to send the data which includes the id of the project, the username of the user whom we are going to send the invitation, the role of user in the project name `Viewer` or `Editor`.

**useMutation**

```
 // mutation to send invitation to selected users
  const [SendInvite, { error: errorB, loading: loadingB }] = useMutation<
    MemberInviteNew
  >(SEND_INVITE, {
    refetchQueries: [{ query: GET_USER, variables: { username } }],
  });
```
In the above mutation, once the invitation is sent(mutation is successful), the `GET_USER` query is refetched to update the data. 

`MemberInviteNew` is the type of data I am sending as variables to mutation string. The interface is defined as follows:

```
export interface MemberInviteNew {
  member: {
    project_id: string;
    user_name: string;
    role: string;
  };
}
```

`SendInvite` is the mutate function which can be called whenever you want to execute the mutation.

```
 SendInvite({
   variables: {
       member: {
             project_id: "1234abc",
             user_name: "john_doe",
             role: "Editor",
               },
              },
            })
```

#**Conclusion**#

So these were some of the examples of GraphQL mutation and query. I hope I was able to explain these concepts well, but if you still have some queries or feedback feel free to reach out to [me](https://www.linkedin.com/in/saranya-jena-7aa24912b). Since the LitmusChaos project is completely open-source, feel free to contribute in any way possible. Visit the [GitHub repo](https://github.com/litmuschaos/litmus) and become one out of the many stargazers.

{% github litmuschaos/litmus %}

Last but not the least, with the upcoming Hacktober Fest, there are many issues for all levels such as good-first issues, front-end issues, complex issues, etc. So even if you are a beginner you can always submit a PR and start contributing to open source. Grab your chance to win a lot of Litmus swags and goodies upon a successful merge. So do not forget to visit the [Litmus site](https://litmuschaos.io/) and join our [community](https://kubernetes.slack.com/messages/CNXNB0ZTN)(#litmus channel on the Kubernetes Slack).😇

![thankyou](https://media.giphy.com/media/l3q2FnW3yZRJVZH2g/giphy.gif)