In 2012, Facebook engineer Nick Schrock started work on a small prototype to facilitate moving away from an old, unsupported partner API that powered the current Facebook News Feed. At the time, this was called “SuperGraph”. Fast forward to today and SuperGraph has helped shape the open-source query language GraphQL, which has been much of the buzzword in recent times.
Facebook describes GraphQL as a “query language for APIs and a runtime for fulfilling those queries with your existing data”. Put simply, GraphQL is an alternative to REST that has been steadily gaining popularity since its release. Whereas with REST a developer would usually collate data from a series of endpoint requests, GraphQL allows the developer to send a single query to the server that describes the exact data requirement.
Want to learn React Native from the ground up? This article is an extract from our Premium library. Get an entire collection of React Native books covering fundamentals, projects, tips and tools & more with SitePoint Premium. Join now for just $9/month.
Prerequisites
For this tutorial, you’ll need a basic knowledge of React Native and some familiarity with the Expo environment. You’ll also need the Expo client installed on your mobile device or a compatible simulator installed on your computer. Instructions on how to do this can be found here.
Project Overview
In this tutorial, we’re going to demostrate the power of GraphQL in a React Native setting by creating a simple coffee bean comparison app. So that you can focus on all of the great things GraphQL has to offer, I’ve put together the base template for the application using Expo.
To get started, you can clone this repo and navigate to the “getting-started” branch, which includes all of our basic views to start adding our GraphQL data to, as well as all of our initial dependencies, which at this stage are:
{
"expo": "^32.0.0",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"react-navigation": "^3.6.1"
}
To clone this branch, you’ll need to open up terminal and run this command:
git clone https://github.com/jamiemaison/graphql-coffee-comparison.git
To then navigate to the getting-started
branch, you move into the newly cloned repo with cd graphql-coffee-comparison
and run git checkout getting-started
.
The next stage is to install our dependencies. To do this, make sure you’re on Node v11.10.1
and run npm install
in the root directory of the project. This will add all of the dependencies listed above to your node_modules
folder.
To start adding GraphQL to our React Native app, we’re going to need to install a few more dependencies that help us perform a few simple GraphQL functions. As is common with modern JavaScript development, you don’t need all of these dependencies to complete the data request, but they certainly help in giving the developer a better chance of structuring some clean, easy-to-read code. The dependencies you’ll need can be installed by running npm install --save apollo-boost react-apollo graphql-tag graphql
.
Here’s an overview of what these dependencies are:
apollo-boost
: a zero-configuration way of getting started with GraphQL in React/React Nativereact-apollo
: this provides an integration between GraphQL and the Apollo clientgraphql-tag
: a template literal tag that parses GraphQL queriesgraphql
: the JavaScript reference implementation for GraphQL
Once all of the necessary dependencies have finished installing, run npm start
. You should now see your familiar Expo window, and if you launch the app (either via a simulator or on a device) then you should see a screen similar to this:
In basic terms, this application has two screens that are managed by react-navigation
, Home.js
and CoffeePage.js
. The Home
screen contains a simple FlatList
that renders all of the coffee beans supplied to its data
field. When clicked on, the user is navigated to the CoffeePage
for that item, which displays more information about the product. It’s our job to now populate these views with interesting data from GraphQL.
Apollo Server Playground
There are two main elements to any successful GraphQL transaction: the server holding the data, and the front-end query making the request. For the purposes of this tutorial, we aren’t going to start delving into the wonderful world of server-side code, so I’ve created our server for us ready to go. All you need to do is navigate to yq42lj36m9.sse.codesandbox.io in your favorite browser and leave it running throughout the course of development. For those interested, the server itself is running using apollo-server
and contains just enough code to hold the data we need and serve it upon receiving an appropriate query. For further reading, you can head over to apollographql.com to read more about apollo-server
.
GraphQL Query Basics
Before we get into writing the actual code that’s going to request the data we need for our coffee bean comparison app, we should understand just how GraphQL queries work. If you already know how queries work or just want to get started with coding, you can skip ahead to the next section.
Note: these queries won’t work with our codesandbox server, but feel free to create your own at codesandbox.io if you’d like to test out the queries.
At its simplest level, we can use a flat structure for our queries when we know the shape of the data we’re requesting:
QUERY: RESPONSE:
{ {
coffee { "coffee": {
blend "blend": "rich"
} }
} }
On the left, we see the GraphQL query requesting the blend
field from coffee
. This works well when we know exactly what our data structure is, but what about when things are less transparent? In this example, blend
returns us a string, but queries can be used to request objects as well:
QUERY: RESPONSE:
{ {
coffee { "coffee": {
beans { "beans": [
blend {
} blend: "rich"
} },
} {
blend: "smooth"
}
]
}
}
Here you can see we are simply requesting the beans
object, with only the field blend
being returned from that object. Each object in the beans
array may very well contain other data other than blend
, but GraphQL queries help us request only the data we need, cutting out any extra information that’s not necessary for our application.
So what about when we need to be more specific than this? GraphQL provides the capability for many things, but something that allows for extremely powerful data requests is the ability to pass arguments in your query. Take the following example:
QUERY: RESPONSE:
{ {
coffee(companyId: "2") { "coffee": {
beans { "beans": [
blend {
} blend: "rich"
} },
} {
blend: "smooth"
}
]
}
}
What we see is that we can pass an argument — in this case, the companyId
— which ensures that we are only returned beans
from one particular company. With REST, you can pass a single set of arguments via query params and URL segments, but with GraphQL querying every single field, it can get its own set of arguments. This allows GraphQL to be a dynamic solution for making multiple API fetches per request.
Aliases
So far, all of our queries have had the fields of the returned object match the name of the field in the query itself. This is great when using simple arguments, but what if you want to query the same field with different arguments in your data request? This is where aliases come in. Aliases let you change the name of a field so you can rename any of the returned data and therefore use different arguments in your data request. Take our coffee bean example. What if we wanted to return data from two different company IDs? We would structure our query like this:
QUERY: RESPONSE:
{ {
company1: coffee(companyId: "1") { "company1": {
beans { "beans": [
blend {
} "blend": "rich"
} }
company2: coffee(companyId: "2") { ]
beans { },
blend "company2": {
} "beans": [
} {
} "blend": "fruity"
}
]
}
}
Here, we request data for aliases company1
and company2
, which are simply different coffee
queries stacked on top of each other. Aliases can be a powerful tool in modifying your requires for your exact data requirement.
Variables
Up until now, we’ve known our exact query and can therefore hardcode it in our application, but most applications will need these fields to be dynamic. For instance, the user might select a coffee bean company from a list to display. We won’t know ahead of time which coffee bean company the user is selecting, so we need a way of passing in these requirements. This is where variables come in.
The GraphQL documentation lists three things that we need to do in order to use variables:
- replace the static value in the query with
$variableName
- declare
$variableName
as one of the variables accepted by the query - pass
variableName: value
in the separate, transport-specific (usually JSON) variables dictionary
In practical terms, this means our data query will look something like this:
query coffeeCompany(companyId: CompanyId) {
coffee(companyId: companyId) {
beans: {
type
}
}
}
We would also pass in companyId
as a JSON object:
{
"companyId": "1"
}
Using variables within GraphQL is a powerful way of making all of our query requests dynamic, as we’re requesting only the data we currently need.
Coffee Query
For the purposes of our application, we’re going to need a query that allows us to request data retrieving only our coffee beans, whilst including all of the relevant fields we’re going to need within that. Our data requirements aren’t that complex, so it will look something like this:
{
coffee {
beans {
key
name
price
blend
color
productImage
}
}
}
Requesting our Data
Now for the actual code that we’re going to use to request our data. Open up App.js
, which is the container for all of our views and will be a good place to make our data request when the app launches.
We’re going to want to initialize our client, which we can simply do by importing ApolloClient from apollo-boost
and specifying our server URL. It’s important to note that you need to have the server initialized, which is achieved by simply running yq42lj36m9.sse.codesandbox.io in your browser. Occasionally you might find the server timing out. If Expo returns a warning that looks something like “network error”, reload yq42lj36m9.sse.codesandbox.io on your browser to re-initialize the server.
Once the server is running, add the imports and initialization to the top of App.js
, which should look something like this:
// ./App.js
import ApolloClient from 'apollo-boost';
const client = new ApolloClient({ uri: 'https://yq42lj36m9.sse.codesandbox.io/' })
Next, we want to assemble the graphQL query for later use when we request our data. Luckily, the graphql-tag
library makes this simple. Again, we need to import the library itself to App.js
:
// ./App.js
import gql from 'graphql-tag';
Now we can structure the query:
// ./App.js
const QUERY = gql`
{
coffee {
beans {
key
name
price
blend
color
productImage
}
}
}
`
The next step is to amend the render function to include our data request. To do this, we use the react-apollo
library to make the request, which allows us to handle the response as we see fit. Add a new import to App.js
:
// ./App.js
import { ApolloProvider, Query } from 'react-apollo';
Then change the render function so that it now looks like this:
// ./App.js
render() {
return (
<ApolloProvider client={client}>
<Query query={QUERY} >
{({ loading, error, data }) => {
if (loading || error) return <View />
return <View style={{ flex: 1 }}>
{this.state.isFontsLoaded ? <AppContainer /> : <View />}
</View>
}}
</Query>
</ApolloProvider>
);
}
Here, you can see we’re using the QUERY
we created earlier to request the necessary data. At this moment in time, we’re simply rendering an empty view whilst loading, and if there’s an error in the data request. In practice, this would be swapped out for the relevant loading and error views, but for this example we’ll leave them blank. Once data is returned, we’re rendering our AppContainer as usual. You can check that data is coming through by checking that data
is being returned successfully. This can be checked by adding a console.log(data)
to your code to view the output in your terminal. You should be receiving an object with our coffee
and beans
fields as long as your Apollo server is running without a problem.
Storing Data with the Context API
We’re going to need somewhere to store our data that will be accessible in any of our components no matter how far down the tree they are. If we were to pass the data all the way through several children just to get to our component, that wouldn’t be the most efficient thing. Given that our data storage needs are fairly simple for this example, it would be good to use React’s Context API rather than some more complex state management tool like Redux. The Context API lets you pass global state down our component tree without the need for passing it through props each time, and for our current example, this is enough.
The benefits of Redux on top of the Context API can broadly be narrowed down to three points:
- Redux comes with a time traveling debugger
- it provides the developer with middleware APIs, giving you access to tools like
redux-sagas
- its React bindings prevent having too many renders
Using the Context API couldn’t be simpler. In essence, we just need to create a <Provider />
component to store all of our data in and access the data by creating a <Consumer />
component when we come to need it.
Creating a Provider
Let’s go back to App.js
, where we only have to add a couple of lines to get our Provider
up and running. First, we’ll start off by creating our AppContext
. We’ll need to access this in any file where we want to use the stored data, so we’ll need to make sure it’s exported. To create the AppContext
, add the following line to App.js
:
// ./App.js
export const AppContext = React.createContext({ data: { coffee: { beans: [] } } });
Here, we’re creating the context and initializing it with some blank data. Next, we want to populate the AppProvider
with the data we’re receiving from the GraphQL server.
Storing cCoffee Data
To update our provider with the data, we simply have to change out the blank container view in our App.js
render function for the provider whilst adding our GraphQL data to its data
prop. This looks like this:
// ./App.js
render() {
return (
<ApolloProvider client={client}>
<Query query={QUERY} >
{({ loading, error, data }) => {
if (loading || error) return <View />
return <AppContext.Provider value={data.coffee.beans} style={{ flex: 1 }}>
{this.state.isFontsLoaded ? <AppContainer /> : <View />}
</AppContext.Provider>
}}
</Query>
</ApolloProvider>
);
}
Here, you can see that we’re directly storing the bean data (data.coffee.beans
) in our provider. At this point we have all of the data necessary, but we’re still rending our placeholder content. The final piece of this puzzle is to amend Home.js
to render our data by using a Consumer
.
Creating an App Consumer
Firstly, we need to import our AppContext
from earlier to make use of the Consumer
. To do this, we simply need to import it from App.js
into Home.js
:
// ./src/Home.js
import { AppContext } from '../App';
Using a Consumer
works like any other React component. For our current purposes, we’ll add it to our render
function and use the data to populate our FlatList
. Our render function should look something like this:
// ./src/Home.js
render() {
return (
<AppContext.Consumer>
{
(context) =>
<View style={styles.container}>
<Image style={styles.header} source={require('../assets/header.png')} />
<View style={{ marginLeft: 30, marginTop: 30 }}>
<Text style={styles.title}>COFFEE</Text>
<Text style={styles.subtitle}>SELECTION</Text>
</View>
<FlatList
style={styles.list}
data={context}
renderItem={({ item }) => <TouchableOpacity style={styles.block} onPress={() => this.props.navigation.navigate('CoffeePage', { item: item })}>
<Image style={styles.productImage} source={{ uri: item.productImage }} />
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.price}>{item.price}</Text>
</TouchableOpacity>}
numColumns={2}
/>
</View>
}
</AppContext.Consumer>
);
}
If we go through the above code, you can see the AppContext.Consumer
provides us with a context
, which contains our GraphQL data. We use this context
to populate the FlatList
component by passing it to the data
prop. When the user clicks on one of the coffee items, our data is passed through navigation params to our CoffeePage.js
, allowing it to be accessed in that view. If you now save your modified files and launch the App on Expo, you should see your fully populated FlatList
.
Summary
Congratulations! You’ve successfully used GraphQL to retrieve data and render that data using React Native. We’ve learned how powerful GraphQL queries can be, whilst highlighting the benefits over a system like REST. I encourage you to use GraphQL in your next project and judge for yourself how much quicker it can be for retrieving data — particularly in data-rich applications.
If you want to explore GraphQL in further detail, I recommend you read the “Queries and Mutations” section of the GraphQL docs, and perhaps start coding an Apollo Server yourself using codesandbox.io.
The entire code for this project can be found on GitHub, so feel free to clone/fork the repo and make your own improvements!
Frequently Asked Questions (FAQs) about GraphQL and React Native
How do I set up Apollo Client in my React Native project?
Setting up Apollo Client in your React Native project involves a few steps. First, you need to install the necessary packages using npm or yarn. These include apollo-boost, react-apollo, and graphql. Once installed, you can create an instance of Apollo Client and link it to your GraphQL endpoint. You’ll also need to wrap your root component with ApolloProvider and pass in the client instance. This allows your React Native components to access the client and make queries or mutations.
How do I make a GraphQL query in React Native?
Making a GraphQL query in React Native involves using the Query component from the react-apollo package. You’ll need to pass your GraphQL query as a prop to this component. The Query component uses the render props pattern, meaning it takes a function as its child. This function will be called with an object containing loading, error, and data properties. You can use these properties to handle the different states of your query and render your UI accordingly.
How do I handle errors when making a GraphQL query?
When making a GraphQL query, errors can be handled using the error property provided by the Query component. This property will be undefined if no errors occurred during the query. If an error did occur, it will be an instance of ApolloError. You can use this error object to display an error message to the user or handle the error in some other way.
How do I use GraphQL mutations in React Native?
GraphQL mutations in React Native are handled using the Mutation component from the react-apollo package. Similar to the Query component, the Mutation component uses the render props pattern. It takes a function as its child, which will be called with a mutate function and an object containing loading, error, and data properties. You can call the mutate function to execute your mutation, and use the other properties to handle the different states of your mutation.
How do I update the Apollo Client cache after a mutation?
Updating the Apollo Client cache after a mutation can be done using the update function provided by the Mutation component. This function is called with the Apollo Client cache and the result of the mutation. You can use these to manually update the cache to reflect the changes made by the mutation. This is useful when you want to ensure that your UI is immediately updated with the results of the mutation.
How do I use GraphQL subscriptions in React Native?
GraphQL subscriptions in React Native are handled using the Subscription component from the react-apollo package. This component works similarly to the Query and Mutation components, using the render props pattern. It takes a function as its child, which will be called with an object containing loading, error, and data properties. You can use these properties to handle the different states of your subscription and render your UI accordingly.
How do I test my GraphQL queries and mutations in React Native?
Testing GraphQL queries and mutations in React Native can be done using tools like Jest and react-testing-library. You can mock the Apollo Client and use it to simulate different states of your queries and mutations. This allows you to test how your components respond to these states and ensure that they render correctly.
How do I handle authentication with GraphQL in React Native?
Handling authentication with GraphQL in React Native can be done using the context option provided by the Apollo Client. You can use this option to include authentication tokens in the headers of your GraphQL requests. This allows your server to authenticate the requests and perform any necessary authorization checks.
How do I paginate data with GraphQL in React Native?
Paginating data with GraphQL in React Native can be done using the fetchMore function provided by the Query component. This function allows you to fetch more data and merge it with the existing data in the Apollo Client cache. You can use this to implement infinite scrolling or load more buttons in your UI.
How do I use GraphQL fragments in React Native?
GraphQL fragments in React Native can be used to split your GraphQL queries into reusable pieces. You can define a fragment using the gql function from the graphql-tag package, and then include it in your queries using the … syntax. This allows you to avoid duplication in your queries and keep your code DRY.
Freelance Software Developer specialising in creating dynamic experiences for web, mobile and desktop. Read more at jamiemaison.com.