Using GraphQL apollo client in Redux-Thunks actions
Tags: react, redux, graphql, architecture
Published: 2020-03-13 15:15:30

Hi everyone 👋

React-Apollo is the go to library to use graphQL alongside React. Their documentation is really cool and comprehensive, but they promote the usage of React Hooks to perform all your requests.
Regarding your frontend architecture, you may not be thrill by the idea of letting all your React components handling these kind of responsibility. 
In my case, we were integrating GraphQL within an existing frontend app with React, React-redux and redux-thunk. The code is written the way that all network API calls are executed via redux actions (using redux thunk).

Assuming you have knowledge of redux-thunk and React-Apollo, I will show some basic example of actions.

For these examples, I will use a public GraphQL API :
- https://github.com/Arjun-sna/directions_graphql/
- https://www.graphqlbin.com/v2/z71NUw

```client.js
import ApolloClient from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
import { setContext } from "apollo-link-context";
import * as UserUtils from "../utils/user";

const cache = new InMemoryCache();
const link = new HttpLink({
  uri: `https://directions-graphql.herokuapp.com/graphql`
});

const authLink = setContext((_, { headers }) => {
  const token = UserUtils.getTokenFromLocalStorage();

  return {
    headers: {
      ...headers,
      authorization: token
    }
  };
});

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

export default client;
```

```actions.js
import gql from 'graphql-tag';
import graphQlClient from "client"

export const signUp = (username = "johndoe", email = "john.doe@test.com", password = "mypassword") => (dispatch, getState) => {
  dispatch(setLoading(true))

  graphQlClient
    .mutate({
      mutation: gql`
        mutation($username: String!, $email: String!, $password: String!) {
          signUp(username: $username, email: $email, password: $password) {
            user {
              id,
              username,
              email
            }
          }
        }
      `,
      variables: { username, email, password },
      update: (_cache, result) => {
        const { data: { signUp: { user: { username, email } } } } = result
        dispatch(signIn(email, password))
      },
    })
    .catch((error) => {
      console.log('error', error)
    })
    .finally(() => {
      dispatch(setLoading(false))
    })
}

const setTokenToLocalStorage = (token) => {
  localStorage.setItem('token', token)
}

const getTokenFromLocalStorage = () =>
  localStorage.getItem("token") || null;

export const signIn = (email, password) => (dispatch, getState) => {
  dispatch(setLoading(true))

  graphQlClient
    .mutate({
      mutation: gql`
        mutation($email: String!, $password: String!) {
          signIn(userIdentifier: $email, password: $password") {
              token
          }
        }
      `,
      variables: { email, password },
      update: (_cache, result) => {
        const { data: { signIn: { token} }} = result
        dispatch(setTokenToLocalStorage(token))
      },
    })
    .catch((error) => {
      console.log('error', error)
    })
    .finally(() => {
      dispatch(setLoading(false))
    })
}

export const direction = (coordinates = {startLat: 50.6333, startLng: 3.0667, endLat: 48.8534, endLng: 2.3488} , travelMode = "driving" ) => (dispatch, getState) => {
  dispatch(setLoading(true))

  graphQlClient.
    graphQlClient.query({
      query: gql`
        input PlaceCoordinatesInput {
          startLat: Float!
          startLng: Float!
          endLat: Float!
          endLng: Float
        }

        enum AllowTravelModes {
          transit
          driving
          walking
        }

        query($coordinates: PlaceCoordinatesInput!, $travelMode: AllowTravelModes!) {
          direction(coordinates: $coordinates, travelMode: $travelMode) {
            steps {
              stepInstruction
            }
          }
        }
      `,
      variables: { coordinates , travelMode },
    }).then((result) => {
      const { data: { direction } } = result
      dispatch(doSomethingWithDirection(direction))
    })    
    .catch((error) => {
      console.log('error', error)
    })
    .finally(() => {
      dispatch(setLoading(false))
    })
}
```

For the sake of the example I used default values for the actions parameters.
As you can see, you easily manipulate graphql apollo client inside redux thunks actions.

To conclude, I will share you some links to help you during your journey of learning GraphQL and react-apollo as well as the repository containing the source code of these examples. 

Documentation :
- https://www.apollographql.com/docs/react/
- https://www.apollographql.com/docs/tutorial/client/

Further reads :
- https://learn.hasura.io/graphql/react
- https://stackoverflow.com/questions/43471623/apollo-client-mutation-error-handling

Source code :
- https://github.com/clementdemily/react-redux-thunks-apollo-graphql