Building a React Native Client App

AWS AppSync integrates with the Apollo GraphQL client for building client applications. AWS provides Apollo plugins for offline support,
authorization, and subscription handshaking. You can use the Apollo client directly,
or you can use it with some of the client helpers provided in the AWS AppSync SDK.

This tutorial shows you how to use AWS AppSync with React Apollo, which uses ReactJS
constructs and patterns with GraphQL. For the latest AWS AppSync SDK documentation,
including API definitions and sample apps, see aws-mobile-appsync-sdk-js on GitHub.

Alternatively, for a simple, lightweight GraphQL client that is integrated into the
AWS ecosystem for authentication, object storage, analytics, and other features, see
AWS Amplify and the GraphQL client.

Before You Begin

This tutorial is set up for a sample API using the schema from the DynamoDB resolvers tutorial. To follow along with the complete flow, you can optionally walk through that tutorial
first. If you want to do more customization of GraphQL resolvers, such as those that
use DynamoDB, see the Resolver Mapping Template Reference. The application uses the following starting schema:

This schema defines a Post type and operations to add, get, update, and delete Post objects.

Get the GraphQL API Endpoint

After you create your GraphQL API, you'll need to get the API endpoint (URL) so you
can use it in your client application. You can get the API endpoint in either of the
following ways:

In the AWS AppSync console, choose Home, and then choose GraphQL URL to see the API endpoint.

Run the following CLI command:

aws appsync get-graphql-api --api-id $GRAPHQL_API_ID

Download a Client Application

To show you how to use AWS AppSync, we first review a React Native application (bootstrapped
with create-react-native-app) with just a local array of data. Then we add AWS AppSync capabilities to it. To
begin, download a sample application where we can add, update, and delete posts.

Understanding the React Native Sample App

The React Native sample app has three major files:

./src/App.js: The main entry point of the application. Renders the main application shell with
two components named AddPost and AllPosts, and has a local array of data named posts that is passed as a prop to the other components.

./src/Components/AddPost: A React Native component that contains a form that enables a user to enter new information
about a post, such as the author and title.

./src/Components/AllPosts: A React Native component that lists all existing posts from the posts array that App.js created. It enables you to edit or delete existing posts.

Run your app as follows, and test it to verify that it works:

yarn && yarn start

Import the AWS AppSync SDK into Your App

In this section, you'll add AWS AppSync to your existing React Native app. Add the
following dependencies to your application:

yarn add react-apollo graphql-tag aws-sdk

Next, add in the AWS AppSync SDK, including the React extensions:

yarn add aws-appsync
yarn add aws-appsync-react

From the AWS AppSync console, go to your GraphQL API integration page at the root
of the navigation bar on the left ). At the bottom of the page, choose JavaScript. Next, click Download Config and save the aws-exports.js configuration file into ./src.

To interact with AWS AppSync, your client needs to define GraphQL queries and mutations.
This is commonly done in separate files, as follows:

You can switch the AUTH_TYPE value to use API keys, IAM (including short-term credentials from Amazon Cognito
federated identities), or Amazon Cognito user pools. We recommend you use either IAM
or Amazon Cognito user pools after onboarding with an API key. The previous code shows
how to use the default configuration of AWS AppSync with an API key, referencing the
aws-exports.js file you downloaded. When you're ready to add other authorization methods to your
application, you can use the AWS Amplify library to quickly add these capabilities to your application. The corresponding AWS Amplify
methods for the AWS AppSync client constructor are included above, and an import of
the library with configuration would look similar to the following:

Test Your Application

yarn start

Offline Settings

There are important considerations that you need to account for if you want an optimistic
UI for an application, where data can be manipulated when the device is in an offline
state. Many of these settings are documented in the official Apollo documentation,
however, we call out several of them here that you should configure.

First, the AWS AppSync client enables you to disable offline capabilities if you simply
want to use GraphQL in an always-online scenario. To do this, you pass an additional
option when instantiating your client, named disableOffline, as follows:

fetchPolicy: This option enables you to specify how a query interacts with the network
versus local in-memory caching. AWS AppSync persists this cache to a platform-specific
storage medium. If you are using the AWS AppSync client in offline scenarios (disableOffline:false), you MUST set this value to cache-and-network:

options: {
fetchPolicy: 'cache-and-network'
}

optimisticResponse: This option enables you to pass a function or an object to a mutation
for updating your UI before the server responds with the result. This is needed in
offline scenarios (and for slower networks) to ensure that the UI is updated when
the device has no connectivity. Optionally, you can use this if you have set disableOffline:true. For example, if you were adding a new object to a list, you might use the following:

Typically, you use optimisticResponse in conjunction with the update option for React Apollo's component, which can trigger during an offline mutation.
If you want the UI to update offline for a specific query, you need to specify that
query as part of the readQuery and writeQuery options on the cache, as shown following:

When this happens, the AWS AppSync persistent store is automatically updated in response
to the Apollo cache update. Upon network reconnection, it will synchronize with your
GraphQL endpoint. You could also modify more than one query when offline, in which
case you could run the process multiple times in the same update block.

Notice that the @aws_subscribe specifies which mutations trigger a subscription. You can add more mutations in this
array to meet your application needs.

The subscription type newPost needs to be passed into an option named updateQuery of the React Apollo client to update your UI dynamically when a subscription is received.
Ensure that this field name matches the subscription type in the following example
code.

In your App.js file, edit the AllPostsWithData HOC to include subscribeToNewPost in the props field, as follows:

Add the following lifecycle method to your AllPosts component in AllPosts.js:

componentWillMount(){
this.props.subscribeToNewPosts();
}

Now try running your app again by typing yarn start. Add a new post via the console, with a mutation on addPost. You should see real-time data appear in your client application.

Conflict Resolution

When clients make a mutation, either online or offline, they can send a version number
with the payload (named expectedVersion) for AWS AppSync to check before writing to Amazon DynamoDB. A DynamoDB resolver
mapping template can be configured to perform conflict resolution in the cloud, which
you can learn about in Resolver Mapping Template Reference for DynamoDB. If the service determines it needs to reject the mutation, data is sent to the client
and you can optionally run an additional callback to perform client-side conflict
resolution.

For example, suppose you had a mutation with DynamoDB set for checking the version,
and the client sent expectedVersion:0, as in the following example:

This would fail the version check because 0 would be lower than any of the current values. You can then define a custom callback
conflict resolver. A custom conflict resolver receives the following variables:

mutation: GraphQL statement of a mutation

mutationName: Optional if a name of a mutation is set on a GraphQL statement

variables: Input parameters of the mutation

data: Response from AWS AppSync of actual data in DynamoDB

retries: Number of times a mutation has been retried

For example, you could have the following custom callback conflict resolver as follows:

In the previous example, you can do a logical check on the mutationName and then rerun the mutation with the correct version that AWS AppSync returned.

Note: We recommend doing this only in rare cases. Usually, we recommend that you let the
AWS AppSync service define conflict resolution to prevent race conditions from occurring.
If you don't want to retry, simply return DISCARD.

Now, to use this callback, pass it into the AWS AppSync client instantiation as follows: