How to integrate GraphQL and Apollo Client in React Native app

Published on May 11, 2020

15 min read

EXPO

Apollo has an entire ecosystem of tools to build GraphQL applications regardless of the frontend framework or library you intend to use. You can use it to develop client-side and server-side apps separately. Apollo has more features and support than its open-source competitors in GraphQL for JavaScript world.

In this tutorial, let us build a small demo app. In this app, you are going to use a third party API endpoint to fetch a list of crypto currencies. This API endpoint is going to be based on REST so a thing you are going to learn is how to make use of Apollo Client to fetch results from a REST endpoint using GraphQL query language.

I am going to use Expo CLI to quickly get started with a React Native project and focus on the purpose of learning, that is, integrating Apollo Client in a React Native app. If you are familiar with React Native CLI, you can go ahead and use it instead of Expo CLI.

Requirements

🔗
  • Nodejs version <= 10.x.x installed
  • watchman installed
  • have access to one package manager such as npm or yarn
  • use react native version 0.60.x or above
  • expo-cli at least 3.11.9

Installing dependencies

🔗

To get started with a new Expo project, run the below command from a terminal window to generate a React Native project based on Expo SDK 36.x.x or above.

npx expo init rnApolloCryptoListDemo
# after the project directory is generated
cd rnApolloCryptoListDemo

I chose Expo over plain React-Native because it includes most of the dependencies that we need so there is less work to do for us.

Let's install all the required dependencies to integrate Apollo Client and request the REST endpoint using GraphQL queries.

yarn add apollo-client apollo-cache-inmemory
graphql-tag apollo-link-rest apollo-link
graphql graphql-anywhere qs @apollo/react-hooks

Lastly, let us install the react-navigation dependencies that will allow us to add a stack navigator to our app. For this, make sure to use expo install.

expo install react-native-gesture-handler react-native-reanimated
react-native-screens react-native-safe-area-context
@react-native-community/masked-view react-navigation-stack

After you run these two commands you should be ready to go. Let's start implementing the app!

Set up a Stack Navigator

🔗

In this section, let us set up a basic navigation flow of our app. The idea you have to implement is that whenever the user touches a coin from the list screen (that is going to be the initial route or screen of the app), the stack navigator pushes another screen to display the details of that particular coin.

To start, create a src/ directory at the root of the project and then inside it, create another directory called screens. In this directory, create two new screen files: CoinsList.js and CoinDetail.js. Each of these screen component files is going to display a title of the screen and a button to navigate from CoinsList to CoinDetail.

Here is the code snippet for screens/CoinsList.js:

1import React from 'react';
2import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
3
4function CoinsList(props) {
5 const { navigation } = props;
6 return (
7 <View style={styles.container}>
8 <Text style={styles.boldText}>Coins List</Text>
9 <TouchableOpacity onPress={() => navigation.navigate('Detail')}>
10 <Text style={styles.boldText}>Go to Detail</Text>
11 </TouchableOpacity>
12 </View>
13 );
14}
15
16const styles = StyleSheet.create({
17 container: {
18 flex: 1,
19 backgroundColor: '#333',
20 justifyContent: 'center',
21 alignItems: 'center'
22 },
23 boldText: {
24 color: '#fff',
25 fontSize: 24,
26 fontWeight: 'bold'
27 }
28});
29
30export default CoinsList;

In the above snippet, observe that props are coming from react-navigation which is yet to set up. Each screen component that becomes the part of the react-navigation flow, has access to these props.

Here is the code snippet for screens/CoinDetail.js:

1import React from 'react';
2import { Text, View, StyleSheet } from 'react-native';
3
4function CoinDetail() {
5 return (
6 <View style={styles.container}>
7 <Text style={styles.boldText}>Coin Detail</Text>
8 </View>
9 );
10}
11
12const styles = StyleSheet.create({
13 container: {
14 flex: 1,
15 backgroundColor: '#333',
16 justifyContent: 'center',
17 alignItems: 'center'
18 },
19 boldText: {
20 color: '#fff',
21 fontSize: 24,
22 fontWeight: 'bold'
23 }
24});
25
26export default CoinDetail;

Now, create another directory src/navigation. Inside it, create a file called MainStackNavigator.js

1import React from 'react';
2import { createStackNavigator } from 'react-navigation-stack';
3
4import CoinsList from '../screens/CoinsList';
5import CoinDetail from '../screens/CoinDetail';
6
7const MainStack = createStackNavigator(
8 {
9 Coins: {
10 screen: CoinsList
11 },
12 Detail: {
13 screen: CoinDetail
14 }
15 },
16 {
17 initialRouteName: 'Coins'
18 }
19);
20
21export default MainStack;

Next, create another file AppNavigator.js with the following code snippet:

1import React from 'react';
2import { createAppContainer } from 'react-navigation';
3
4import MainStack from './MainStackNavigator';
5
6export default createAppContainer(MainStack);

Lastly, to see this navigational flow in action, open App.js file and make sure you import AppNavigator from AppNavigator.js file.

1import React from 'react';
2
3import AppNavigator from './src/navigation/AppNavigator';
4
5export default function App() {
6 return <AppNavigator />;
7}

That's it. Our basic navigational flow is ready to be tested. Open the terminal window and execute expo start. Open your choice of simulator or device and make sure it has an Expo client as an app installed.

Here is the demo of the navigational flow we have set up so far.

How to configure Apollo Client in a React Native app

🔗

In this section, let us integrate Apollo Client such that we are able to fetch the data from the REST endpoint. Start by creating a new directory src/graphql inside which also create a new file Client.js.

The apollo-client package along with apollo-cache-inmemory and apollo-link is a fully-featured GraphQL client that can be integrated into React or React Native apps. Let us import all three of them inside this file.

1import { ApolloClient } from 'apollo-client';
2import { InMemoryCache } from 'apollo-cache-inmemory';
3import { RestLink } from 'apollo-link-rest';

The apollo-link-rest package allows you to use third-party APIs that do not have GraphQL endpoints or have REST endpoints but what you want to transmit them into GraphQL.

The API endpoint we are going to use is a REST endpoint from CryptoCompare.com. Make sure, at this point, you have access to the API Key (that is free at the time of writing this tutorial). Their API offers many endpoints for different use case but we are going to use fetch a number of top coins by their total volume across all markets in the last 24 hours.

Add a RestLink for the Rest API endpoint and pass headers which is an object representing values to be sent as headers on the request. The value you need to sent while requesting data from the API endpoint is the API key.

1const restLink = new RestLink({
2 uri: 'https://min-api.cryptocompare.com',
3 headers: {
4 Authorization:
5 'd251970548f7321b548d3fb61d58c1a456974ea02ba41437fc9bf711f4e89782'
6 }
7});

Add the following configuration with the default cache and RestLink to complete the configuration of Apollo Client.

1export const client = new ApolloClient({
2 link: restLink,
3 cache: new InMemoryCache()
4});

Now, open the App.js file to wrap the current root of the app (<AppNavigator/>) with ApolloProvider. This provider is similar to React's Context.Provider and places the Apollo Client on the context. This makes them accessible to Apollo Client easily and from anywhere inside the component tree.

Start by importing client from src/graphql/Client.js and ApolloProvider from @apollo/react-hooks and use it to wrap AppNavigator.

1import React from 'react';
2
3import AppNavigator from './src/navigation/AppNavigator';
4import { ApolloProvider } from '@apollo/react-hooks';
5import { client } from './src/graphql/Client';
6
7export default function App() {
8 return (
9 <ApolloProvider client={client}>
10 <AppNavigator />
11 </ApolloProvider>
12 );
13}

Writing your first GraphQL query

🔗

In this section, let us write a query to hook the Apollo Client to fetch results from the REST API endpoint. However, the query is going to be made in GraphQL query language with the help of graphql-tag.

In the src/graphql directory, create a new file called Queries.js and import graphql-tag.

1import gql from 'graphql-tag';

Export FETCH_COIN_LIST using a template from the gql tag. Add a query that is going to fetch the top cryptocurrency list from the API endpoint. Using the @rest directive Apollo manages how to parse this query.

1export const FETCH_COIN_LIST = gql`
2 query FetchCoinsList {
3 coinsList
4 @rest(type: "ListPayload", path: "/data/top/totalvolfull?tsym=USD") {
5 Data @type(name: "DataPayload") {
6 CoinInfo @type(name: "CoinInfoPayload") {
7 Id
8 Name
9 FullName
10 }
11 DISPLAY @type(name: "DisplayPayload") {
12 USD @type(name: "USDPayLoad") {
13 PRICE
14 OPENDAY
15 HIGHDAY
16 LOWDAY
17 OPEN24HOUR
18 }
19 }
20 }
21 }
22 }
23`;

The data structure fetched from this API endpoint in JSON format looks like below:

Make a request to REST endpoint with Apollo Client

🔗

Open the file screens/CoinsList.js and import the FETCH_COIN_LIST query as well as the useQuery hook from @apollo/react-hooks.

1import React, { useEffect } from 'react';
2import { useQuery } from '@apollo/react-hooks';
3import { FETCH_COIN_LIST } from '../graphql/Queries';

In the above code snippet, let us import React hook useEffect just to test that the endpoint is fetching data as per our needs. The data fetched is going to be displayed in a console statement that you can view using Remote JS Debugger and Console tab in the Developer Tools of a web browser.

The hook useQuery can be used to make the request to the API endpoint by referencing the query FETCH_COIN_LIST.After being called, it returns a result object with a set of properties. We only need two properties for now: loading and data. Destructure this query hook inside the CoinsList functional component as shown below.

1function CoinsList(props) {
2 // ...
3 const { loading, data } = useQuery(FETCH_COIN_LIST);
4 // ...
5}

Then, using useEffect to, you can fetch the result from the Query.

1function CoinsList(props) {
2 // ...
3 useEffect(() => {
4 console.log(data);
5 }, []);
6 // ...
7}

Make sure the expo start command is running from the terminal window. Then, go the Expo client either on a real device or a simulator, open the developer menu on a Mac using:

  • if on iOS simulator, press Ctrl-Cmd-Z
  • if on Android emulator, press Cmd+M
  • if using a real device, just shake your device a bit

This is what the developer menu in an Expo client looks like:

Choose the option Debug Remote JS. A debugger like below should appear in your default web browser.

Open the Console tab from the Developer Tools of the web browser. You are going to get the following result.

That's it! The Apollo integration is working and you can start displaying the data in the app.

Add an ActivityIndicator while fetching results

🔗

The useQuery hook gives one property called loading that can be used to indicate on the device's screen when the query is in the process of fetching the result. Using the ActivityIndicator, a loading indicator can be displayed.

Import the ActivityIndicator component from react-native in the file CoinsList.js.

1import { Text, View, StyleSheet, ActivityIndicator } from 'react-native';

Next, modify the return statements as follows.

1if (loading && !data) {
2 return (
3 <View style={styles.loadingIndicatorContainer}>
4 <ActivityIndicator size="large" color="#fff" />
5 </View>
6 );
7}
8return (
9 <View style={styles.container}>
10 <Text style={styles.boldText}>Coins List</Text>
11 </View>
12);

Lastly, do not forget to add the corresponding styles for the View that wraps the loading indicator.

1const styles = StyleSheet.create({
2 // ... rest of the styles
3 loadingIndicatorContainer: {
4 flex: 1,
5 backgroundColor: '#333',
6 justifyContent: 'center',
7 alignItems: 'center'
8 }
9});

Now go back to the Expo client and see the result yourself.

Display data in a list using FlatList

🔗

To display a list of items, let us create a separate component that can be reused for a different purpose if the scope of this app gets larger.

Create a new directory called src/components within a new file ListItem.js.

This component is going to display the name, full name and the price of the component, all inside a touchable button that is going to navigate to the Detail screen you created earlier.

Begin by importing the core components from react-native.

1import React from 'react';
2import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';

With some destructuring from the incoming props coin (that is going to be passed from CoinsList.js soon), add a functional component called ListItem.

1function ListItem(props) {
2 const { coin, onPress } = props;
3 const { CoinInfo, DISPLAY } = coin;
4 const { FullName, Name } = CoinInfo;
5
6 return (
7 <TouchableOpacity
8 style={styles.container}
9 onPress={() => onPress && onPress(coin)}
10 >
11 <View style={styles.row}>
12 <Text style={styles.text} numberOfLines={1}>
13 {Name}
14 </Text>
15 <View style={styles.right}>
16 <Text style={styles.text} numberOfLines={1}>
17 {DISPLAY.USD.PRICE}
18 </Text>
19 </View>
20 </View>
21
22 <View style={styles.row}>
23 <Text style={[styles.text, styles.name]} numberOfLines={1}>
24 {FullName}
25 </Text>
26 </View>
27 </TouchableOpacity>
28 );
29}

Add the corresponding styles to the above component and do not forget to export it.

1const styles = StyleSheet.create({
2 container: {
3 flex: 1,
4 padding: 20
5 },
6 active: {
7 backgroundColor: 'rgba(255,255,255,0.05)'
8 },
9 row: {
10 flexDirection: 'row',
11 justifyContent: 'space-between'
12 },
13 right: {
14 flex: 1,
15 alignSelf: 'flex-end',
16 alignItems: 'flex-end'
17 },
18 text: {
19 color: '#FFFFFF',
20 fontSize: 24,
21 fontWeight: '500'
22 },
23 name: {
24 color: 'rgba(255,255,255,0.5)',
25 fontSize: 16,
26 fontWeight: '300'
27 }
28});
29
30export default ListItem;

Now, import this component inside CoinsList.js.

1import ListItem from '../components/ListItem';

Also, import the FlatList component that is going to render the list of coins, from the react-native core.

1import { View, StyleSheet, FlatList, ActivityIndicator } from 'react-native';

Now, add this FlatList component wrapped inside the root View component like below.

1return (
2 <View style={styles.container}>
3 <FlatList
4 contentContainerStyle={styles.contentContainerStyle}
5 data={data.coinsList.Data}
6 keyExtractor={item => item.CoinInfo.Id.toString()}
7 renderItem={({ item }) => {
8 return (
9 <ListItem
10 coin={item}
11 onPress={() => navigation.navigate('Detail', { coin: item })}
12 />
13 );
14 }}
15 />
16 </View>
17);

Modify the container styles.

1const styles = StyleSheet.create({
2 container: {
3 flex: 1,
4 backgroundColor: '#333'
5 }
6 // ... rest of the styles remain same
7});

Now, go back to the simulator device and you are going to get the following result.

On touching one of the coins from the list, it is going to take the user to the Detail screen which for now, does not contain any details. This is navigation is done by using navigation props from the react-navigation library. Do note that, the params coin is also getting passed. This will be useful when displaying the data on the Detail screen from the same GraphQL query.

For now, you can see that the navigation works.

Completing the Detail Screen

🔗

Since all the props are being passed from the CoinsList to the Detail screen as well as the navigation pattern working, let us set up the Detail screen now.

Open screen/CoinDetail.js file and start by importing the following core components.

1import React from 'react';
2import { Text, View, StyleSheet } from 'react-native';

Then, using some destructuring from the props, add them to the functional component CoinDetail.

1function CoinDetail(props) {
2 const { navigation } = props;
3 const { state } = navigation;
4 const { params } = state;
5 const { coin } = params;
6 const { CoinInfo, DISPLAY } = coin;
7 const { FullName, Name } = CoinInfo;
8 const { USD } = DISPLAY;
9 const { PRICE, OPENDAY, HIGHDAY, LOWDAY, OPEN24HOUR } = USD;
10
11 // ...
12}

After the destruction, add the JSX to be returned.

1return (
2 <View style={styles.container}>
3 <View style={styles.header}>
4 <Text numberOfLines={1} style={styles.text}>
5 {Name} - {FullName}
6 </Text>
7 <Text style={styles.priceText} numberOfLines={1}>
8 Price: {PRICE}
9 </Text>
10 </View>
11 <View style={styles.statsContainer}>
12 <View>
13 <View style={styles.statRow}>
14 <Text style={styles.stat} numberOfLines={1}>
15 Open Day
16 </Text>
17 <Text style={styles.stat} numberOfLines={1}>
18 {OPENDAY}
19 </Text>
20 </View>
21 <View style={styles.statRow}>
22 <Text style={styles.stat} numberOfLines={1}>
23 Highest in a day
24 </Text>
25 <Text style={styles.stat} numberOfLines={1}>
26 {HIGHDAY}
27 </Text>
28 </View>
29 <View style={styles.statRow}>
30 <Text style={styles.stat} numberOfLines={1}>
31 Lowest in a day
32 </Text>
33 <Text style={styles.stat} numberOfLines={1}>
34 {LOWDAY}
35 </Text>
36 </View>
37 <View style={styles.statRow}>
38 <Text style={styles.stat} numberOfLines={1}>
39 Open in 24 hours
40 </Text>
41 <Text style={styles.stat} numberOfLines={1}>
42 {OPEN24HOUR}
43 </Text>
44 </View>
45 </View>
46 </View>
47 </View>
48);

Lastly, do not forget to add the corresponding styles to the above JSX using the StyleSheet object.

1const styles = StyleSheet.create({
2 container: {
3 flex: 1,
4 backgroundColor: '#fff'
5 },
6 header: {
7 flex: 30,
8 justifyContent: 'center',
9 alignItems: 'center'
10 },
11 text: {
12 fontSize: 32,
13 color: '#161616'
14 },
15 priceText: {
16 fontSize: 24,
17 color: '#161616'
18 },
19 statsContainer: {
20 flex: 62,
21 backgroundColor: '#161616'
22 },
23 statRow: {
24 padding: 10,
25 flexDirection: 'row',
26 justifyContent: 'space-between'
27 },
28 stat: {
29 color: '#fff',
30 fontSize: 16,
31 fontWeight: '500'
32 }
33});

On visiting the detail of any Coin in the list, the following is going to be displayed.

Display coin name in the header in the Detail screen

🔗

The last piece of functionality that you could add to make the current app look better is to display the name of the coin instead of Detail.

To do this, all you have to do is add the following snippet:

1CoinDetail.navigationOptions = screenProps => ({
2 title: screenProps.navigation.getParam('coin').CoinInfo.Name
3});

Each screen component in your app is provided with the navigation prop automatically. This prop contains various convenience functions that dispatch navigation actions on the route's router. Using screenProps, this library allows you to access the props of the current screen.

Now, go back to the app and open the details of any coin from the list rendered. You are going to notice that the short name of the coin is displayed in the header as shown below.

Conclusion

🔗

That's it for this tutorial. I hope you learned how to use rest endpoint and integrate Apollo Client to any API endpoint to query desired results in a React Native and Expo app. Moreover, I hope you have seen a good use case of creating stack navigators too from the react-navigation library.

Originally published at Crowdbotics' blog.


More Posts

Browse all posts

Mico Dan

I'm a FullStack Developer and a technical writer. In this blog, I write about Technical writing, Node.js, React Native and Expo.