React Native, AWS AppSync and React Apollo

Here at Gravitywell, we have a strong belief in the benefits of hackathons. They’re a regular occurrence for us (4 or 5 times a year), with our most recent project focused on creating a hyper-local coffee delivery app. I was tasked with building the mobile app that would allow the user to view available products, order a coffee and make payments. During the planning phase, we had decided to use AWS Appsync for our GraphQL server and so I would need a way to query the data in the app. There are a number of options available when it comes to querying a GraphQL API in React Native.

I started the work with the intention of sticking to what AWS offers from their Amplify package using either the Amplify GraphQL client or the AppSync SDK. This seemed like the logical idea and felt like a good opportunity to try a new library. I had used React Apollo in previous projects but not with React Native. I had always had a great experience.

However, a few days into the hackathon I was having issues with the AWS offerings that made me pine for React Apollo. Having weighed up the hit we would take by refactoring the already written AWS GraphQL code, deciding it was worth it, I started rewriting the data layer of the app.

I definitely did not regret it.

The rest of this post…

The rest of this post is going detail how to get up and running with AWS AppSync and React Apollo in a React Native project.

First, we need to create a new React Native project and move into that new project directory.

react-native init appsyncApollo
cd appsyncApollo

Let's get the app built just to make sure everything worked as expected:

react-native run-ios

hello old friend:

Before we proceed much further we need to make sure we have the aws-amplify cli installed and configure to be able to create the resources in our AWS account.

yarn global add @aws-amplify/cli
amplify configure

Amplify’s configure CLI will walk you through everything you need todo to get set up. Nader Dabit has explained this much better than I could so check it out.

AWS Amplify configure walkthrough

Next, we’ll install the apps first lot of dependencies. AWS amplify is the Javascript library that makes interacting with AWS super simple. Then a supporting library that provides some conveniences when working with Amplify and React Native. We also need to link the auth library with its native counterparts. Finally we need to initialise Amplify in our project.

yarn add aws-amplify aws-amplify-react-native
react-native link amazon-cognito-identity-js
amplify init

With that done we can start adding AWS services to our app. First, we are going to add authentication. Add the following and select the Default Configuration. Then push that to AWS

amplify add auth
amplify push

Now open ./App.js and include the Amplify library and the withAuthenticator HOC. This gives us a complete authentication solution in a couple of line of code. It's pretty mind-blowing how easy Amplify makes it do complex tasks like authentication.

import Amplify from 'aws-amplify';
import awsmobile from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react-native';

Amplify.configure(awsmobile);

And then wrap App with the withAuthenticator. If you are following along you will need to change how the class in App.js is exported.

export default withAuthenticator(App, true);

You will now be greeted with the login screen.

Go ahead and create an account and login.

At the time of writing, there was a small bug in the aws-amplify-react-native package, where a font is not found. If this is the case you can do the following:

yarn add react-native-vector-icons
react-native link react-native-vector-icons

With that initial set up done. Let’s create our first GraphQL API. We are gonna use the Amplify CLI to guide us through creating the endpoint.

amplify add api

When the editor opens add completed: Boolean to the type. It will look like this. Press enter when you have made your changes.

type Todo @model {
id: ID!
name: String!
description: String
completed: Boolean!
}

The @model is a directive used by GraphQL Transform to create our API. Take a look at the docs for further investigation.

Finally, we need to publish our API. Run amplify push. You will be asked a number of questions regarding your schema generation. It should look like this:

Now that our API has been generated we can dive into the app and start using React Apollo to query our data.

Let’s install the packages needed for the use of React Apollo and AppSync. aws-appsync-react and aws-appsync are needed to allow the use of AWS AppSync. graphql-tag andreact-apollo allows us to query the data using React Apollo

yarn add aws-appsync aws-appsync-react graphql-tag react-apollo

First thing we need to do is connect AppSync to Apollo. Open ./App.js and update the contents to this:

The key part of this file is AWSAppSyncClient which connects Apollo to AppSync using the credentials created and stored by Amplify when we logged in.

First Mutation

Let's write our first mutation. We are gonna write the mutation to create a new todo. Without this, our app is pretty useless.

Let's create a new file at ./components/CreateTodo.js and add the following code to it.

This is where all the create todo code goes. We create a standard class component and add some initial state. This gives us somewhere to add our form values. We usegql to create the CREATE_TODO_MUTATION that we will pass to the Mutation component provided by react-apollo. Our mutation only accepts 2 values as the completed prop will always be set to false when a new todo is created. The Mutation component takes the mutation we just wrote and a variables prop. Here we just spread state into the variables prop, passing the data to the mutation.

Within the mutations render prop we include a simple form and call the createTodo mutation in the onPress of the forms button.

We also need to update our App.js to show this new component. Update its content to this:

Query

The ListTodos component follows a similar pattern to the CreateTodos component.

Let's create the ./components/ListTodos.js file and add the following to it:

As you can see. We create a simple functional component that contains a flat list, listing out each of the todo items. The items come are passed from the render prop created by the Query component.

Again we use gql to create the query that we pass to the Query component. Now we need to add this newly created component to our App.js so that the list is rendered above the form. Update the render function in App.js to match this.

Your app should look something like this.

If you have played with creating todos you may have noticed that your new todos are not added to the list. We need a way to get any new todos as they are created.

React Apollo gives us exactly this in the form of refetchQueries. refetchQuery takes an array of queries to refetch once the mutation has completed. This should update the Apollo cache and add the new todo to the list

The eagle-eyed amongst you will have noticed that in the ListTodos component we also export the LIST_TODOS_QUERY this allows us to reuse it in the CreateTodo component. We add the refetchQueries prop to our mutation, refetching the todos which automatically updates the todos list.

import { LIST_TODOS_QUERY } from './listTodos';
[...]
<Mutation
mutation={CREATE_TODO_MUTATION}
variables={{ ...this.state }}
refetchQueries={[{ query: LIST_TODOS_QUERY }]}>
{(createTodo, { data }) => {
console.log("MUTATION DATA: ", data);
return (
<View style={{ flex: 1, width: "100%" }}>
[...]

There is plenty more that can be done with this example. Why don't you try adding the mutation to update the completed status of the todos or add the ability to delete a todo? You have all the tools in your arsenal to tackle most graphQL situations in your React Native Apps.

Bonus tip: If you use the awesome React Native Debugger then you can also use the Apollo dev tools straight from there. Adds a new level of awesome to the development experience. You have access your schema and have the ability to test mutations and queries from the dev tools and Apollo will handle passing in your auth creds.

Quick note: Throughout this tutorial, we have written our own queries, but with Amplify and AppSync when you create an API you get provided with a generated file of possible queries and mutations. These can be used in place of your hand coded queries or mutations and are passed in the following way:

import { listTodos } from './src/graphql/queries'
[...]
<Query query={gql(listTodos)}>
{({ data: { listTodos }, error, loading }) => {
[...]

Making the decision to refactor to Apollo was definitely worth it. That is not to say that the Amplify offerings were not great, they just did not suit the needs of the application at hand. React Native, AppSync and React Apollo is a winning combination.

The source code for this tutorial can be found here.

See what we have done for others and get in touch to discuss what we can achieve together