How to build React Native apps with GraphQL and Apollo

2020-01-09

GraphQL is described as a query language for APIs. It is also considered as an alternative to REST and has been adapted more frequently in the last few years. Do you have a GraphQL endpoint already setup but you are looking forward to gaining some insight on how to gracefully consume this endpoint in a React Native app?

Together, let us build a demo app that leverages an integration between Apollo Client, React Native and Expo.

Apollo has an entire ecosystem based on to build GraphQL applications. It could be used 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 for now.

Objectives

The main objectives of this tutorial going to cover are:

  • Learn how to integrate the Apollo Client in a React Native app
  • Fetch data from a REST Endpoint with Apollo using GraphQL query language
  • Show a loading indicator when results are being fetched
  • Display results from the remote API endpoint

Requirements

Before we get started, make sure you have installed the following on your local development machine.

  • Nodejs <= 10.x.x
  • expo-cli
  • Access to API Key at NewsAPI.org (it's free)

Do note that, to follow along with this tutorial, you need some basic knowledge of React Native and Expo SDK. You are also required to have Expo Client installed either on a simulator device or a real device.

Throughout this tutorial, I am going to rely on and use the iOS simulator. The demo app we are constructing should work on Android devices as well.

Generating an Expo app

Create a new React Native/Expo app using the following command. Make sure to navigate to the directory once the project has generated.

1npx expo init rn-apollo-demo
2
3cd rn-apollo-demo

When generating an Expo app, you are prompted some questions by the cli interface as shown below. Make sure you choose expo-template-blank. The rest of the questions can have default answers as suggested.

[fl1]

This tutorial assumes that you are using the latest Expo SDK version (36.x.x and above ).

The next step is to install dependencies that we are going to use to build this app. You will learn about each individual dependency whenever necessary in the rest of the tutorial. Do note that, some of these dependencies are peer dependencies and you might not use them directly. Open a terminal window and install the following.

1yarn add apollo-client apollo-cache-inmemory graphql-tag apollo-link-rest apollo-link graphql graphql-anywhere qs

After the dependencies have installed, open App.js file from the root of the project in your favorite code editor or IDE. Let us replace some of the default contents with some meaningful UI components such as custom header.

1// App.js
2import React from 'react'
3import { StyleSheet, Text, View } from 'react-native'
4
5const Header = () => (
6 <View style={styles.header}>
7 <Text style={styles.headerText}>News App</Text>
8 </View>
9)
10
11function App() {
12 return (
13 <View style={styles.container}>
14 <Header />
15 <View style={styles.contentContainer}>
16 <Text>Open up App.js to start working on your app!</Text>
17 </View>
18 </View>
19 )
20}
21
22const styles = StyleSheet.create({
23 container: {
24 flex: 1,
25 backgroundColor: '#fff'
26 },
27 header: {
28 marginTop: 50,
29 alignItems: 'center',
30 borderBottomWidth: StyleSheet.hairlineWidth
31 },
32 headerText: {
33 marginBottom: 5,
34 fontSize: 30,
35 fontWeight: 'bold'
36 },
37 contentContainer: {
38 paddingHorizontal: 20,
39 paddingVertical: 20
40 }
41})
42
43export default App

To make sure everything works, open the terminal window. Execute the command expo start that triggers the Expo client app to run. Press i if you are using an iOS simulator or a if you are using an android emulator.

When the App component renders for the first time, you are going to get the following output:

[fl2]

How to consume a REST endpoint using Apollo Client

With the base app running, you are ready to build further. Let us explore a way to configure the Apollo client in our app in this section. Create a new file called Client.js inside a directory src/graphql. The configuration part is going to fit inside this file. Import the following statements to begin the configuration process.

1// src/graphql/Client.js
2import { ApolloClient } from 'apollo-client'
3import { InMemoryCache } from 'apollo-cache-inmemory'
4import { RestLink } from 'apollo-link-rest'

Three different packages are being imported in the above code snippet. The apollo-client and apollo-cache-inmemory are used to integrate GraphQL client in a React or React Native app. Another package required to make it work is called apollo-link. But in our app, we are going to use a variant called apollo-link-rest.

Why the variant? The reason being is that this package allows the app to use third-party APIs that do not have a GraphQL endpoint. That means, they tend to have a REST endpoint. One real-time API is NewsAPI. This package is going to help us transmit the query to the REST endpoint in a GraphQL query.

In the Client.js file, start by adding a link from the constructor RestLink and pass the API key as field inside headers object. This object represents the value to be sent as headers on a request.

1const restLink = new RestLink({
2 uri: 'https://newsapi.org/v2/',
3 headers: {
4 Authorization: '47e036d83ccc4058b1f85362bc2be1f4'
5 }
6})

Lastly, add the below 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})

Fetching results from a Rest endpoint using Apollo

Create a new file called Queries.js inside src/graphql directory. This file is going to contain the structure of the GraphQL query which is eventually going to fetch the result from the API.

Start by importing gql from graphql-tag.

1import gql from 'graphql-tag'

Export a new query called Headlines as shown in the snippet below. This query is going to fetch top articles with fields such as title, the published location, the URL of the article and so on. Using the @rest directive, Apollo client knows how to parse the query.

1export const Headlines = gql`
2 query TopHeadlines {
3 headlines
4 @rest(
5 type: "HeadlinesPayload"
6 path: "top-headlines?country=us&category=technology"
7 ) {
8 totalResults
9 articles @type(name: "ArticlePayload") {
10 title
11 publishedAt
12 url
13 urlToImage
14 source @type(name: "SourcePayload") {
15 name
16 }
17 }
18 }
19 }
20`

Fetching results from the query

To use the previously created query, import it as well as the configured Apollo client inside the App.js file.

1import { client } from './src/graphql/Client'
2import { Headlines } from './src/graphql/Queries'

Using the query Headlines the result should be fetched from the API whenever the component mounts as well as on the initial render of the app. This can be done by using the React Hook useEffect.

Create a handler method requestHeadlines to invoke the query using Apollo Client. Invoking the query is simply done adding a promise. I am going to console.log the result from the query for now.

1// do ont forget to import useEffect
2import React, { useEffect } from 'react'
3
4// inside App functional component
5useEffect(() => {
6 requestHeadlines()
7}, [])
8
9const requestHeadlines = () => {
10 client
11 .query({
12 query: Headlines
13 })
14 .then(response => {
15 console.log('RESPONSE ==>', response)
16 })
17 .catch(error => {
18 console.log('ERROR ==>', error)
19 })
20}

With the Expo client, there is an option to Debug JS remotely. It opens the console tab in browser’s developer tools and lets you see results of log statements (just like in web development).

Here is the result of the above query. When invoked, it fetches 20 headline objects in an array with all requested fields.

[fl3]

Add a loading indicator

In the last image, in the response, you will find a field called loading that indicates are the results being fetched or the response is complete. Using this can be helpful when adding an activity indicator to display the same message to the end-user in a real mobile app.

Start, by importing the ActivityIndicator component from react-native. Also, to set a state for the loading indicator, import the hook useState.

1import React, { useState, useEffect } from 'react'
2import { StyleSheet, Text, View, ActivityIndicator } from 'react-native'

Inside the App component define the state for loading.

1const [loading, setLoading] = useState(true)

Modify the return statement by adding an if-else statement based on the boolean value of the loading.

1if (loading) {
2 return (
3 <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
4 <ActivityIndicator size="large" />
5 </View>
6 )
7} else {
8 return (
9 <View style={styles.container}>
10 <Header />
11 <View style={styles.contentContainer}>
12 <Text>Open up App.js to start working on your app!</Text>
13 </View>
14 </View>
15 )
16}

After this step, you are going to get the following output:

[fl4]

To make it stop and behave in the way the app requires, update the state of loading when the promise resolves inside the query handler method requestHeadlines.

1.then(response => {
2 console.log('RESPONSE ==>', response)
3 setLoading(response.loading)
4 })

Refresh the Expo client and you will notice that after a few seconds, when the data is fetched, the loading indicator disappears and the screen UI is displayed as before.

Display headlines from the API

You are all set to display individual article component that is being fetched from the API endpoint. Add another state variable called articles. To render a list of data, let’s use the FlatList component from react-native.

1import {
2 StyleSheet,
3 Text,
4 View,
5 ActivityIndicator,
6 FlatList
7} from 'react-native'
8
9// inside App component add another state variable called articles
10const [articles, setArticles] = useState([])

Update the articles state with the array from the response when the promise gets resolved inside the handler method requestHeadlines.

1const requestHeadlines = () => {
2 client
3 .query({
4 query: Headlines
5 })
6 .then(response => {
7 console.log('RESPONSE ==>', response)
8 setLoading(response.loading)
9 setArticles(response.data.headlines.articles)
10 })
11 .catch(error => {
12 console.log('ERROR ==>', error)
13 })
14}

The FlatList component requires three attributes to render a list of data items.

  • data is an array of data that is provided by the state variable articles.
  • renderItem contains the JSX for each item in the data array for which let’s create a new component called Article in a separate file in the next section.
  • keyExtractor is used to extract a unique key for a given item at the specified index for which let’s use each article’s URL for that is going to be unique.
1return (
2 <View style={styles.container}>
3 <Header />
4 <View style={styles.contentContainer}>
5 <FlatList
6 data={articles}
7 renderItem={({ item }) => <Article {...item} />}
8 keyExtractor={item => `${item.url}`}
9 />
10 </View>
11 </View>
12)

Add an Article component

Inside the src/components directory create a new file called Article.js. This component is going to display the title and source of each headline.

The Article component is going to be a presentation component that receives everything from the App component as props. Add the following to the file.

1import React from 'react'
2import { View, Text, StyleSheet } from 'react-native'
3
4const Article = ({ title, source }) => (
5 <View style={styles.content}>
6 <Text style={styles.source}>{source.name}</Text>
7 <Text style={styles.title}>{title}</Text>
8 </View>
9)
10
11const styles = StyleSheet.create({
12 content: {
13 marginLeft: 10,
14 flex: 1
15 },
16 source: {
17 color: '#3d3c41',
18 fontSize: 14,
19 fontWeight: '500',
20 marginBottom: 3
21 },
22 title: {
23 fontSize: 18,
24 fontWeight: 'bold',
25 marginBottom: 15
26 }
27})
28
29export default Article

Import the Article component in App.js file for it to work.

1import Article from './src/components/Article'

Go to the simulator or the device that is running the Expo client and you are going to see several headlines being fetched.

[fl5]

Conclusion

That's it! Congratulations to you for sticking around till the end. I hope this post serves as the basis of your adventure in building React Native apps with GraphQL and Apollo.

Originally published at Newline.co/Fullstack.io

I'm Aman working as an independent fullstack developer with technologies such as Node.js, ReactJS, and React Native. I try to document and write tutorials to help JavaScript, Web and Mobile developers.