AWS Mobile Blog

Building progressive web apps with the Amplify Framework and AWS AppSync

This article was written by Rob Costello, Solutions Architect, AWS

Many organizations regularly collect valuable data about employees’ or customers’ experiences or concerns using polls or surveys. For this scenario, a client application should perform the following tasks:

  • Present different types of questions
  • Collect responses from varying sets of users
  • Store data (and metadata) for analysis
  • Authenticate users
  • Secure data appropriately when transmitting to a backend service

Users may see this as intrusive and time-consuming unless the experience is simple and does not present any challenges in completing the questions at their convenience.

This post describes how to build progressive web apps (PWAs) that use the React JavaScript library, GraphQL, AWS AppSync, the Amplify Framework, and other AWS services to form a complete solution.

I explore a pre-built PWA that provides this functionality:

  • Using the Amplify Framework with AWS AppSync to build a backend GraphQL service that uses Amazon Cognito for user authentication and Amazon DynamoDB for storage
  • Using the AppSync SDK to provide offline-first support to a React-based web application
  • Using GraphQL queries, mutations, and subscriptions to interact with AWS AppSync
  • Using Amazon Pinpoint to collect web analytics data
  • Deploying your application using the Amplify Console

What are PWAs (Progressive Web Apps)?

PWAs are web applications that use modern browser and operating system features to provide rich user experiences. PWAs have the following key attributes:

  • They are reliable. PWAs should function for end users regardless of their network connectivity state. They should work when online, offline, and everywhere in between.
  • They are fast. PWAs should load fast and respond quickly to user interactions.
  • They are engaging. PWAs can be installed on mobile and desktop devices (without an app store), and run full screen or “out of browser” on users’ devices, providing an experience similar to that of native applications.
  • They are secure. PWAs must be served over HTTPS to function correctly.

PWAs offer benefits not only to end users, but also to application developers, allowing multi-platform development, simple deployment, and low barriers to entry. Development teams with existing skills in modern web application development can transition to PWAs with minimal new learning required.

Prerequisites

This solution uses several AWS services, so you need an AWS Account with appropriate permissions to create the related resources.

On your development workstation, you need the following:

  • Node.js with npm
  • AWS CLI with output configured as JSON (pip install awscli --upgrade --user)
  • AWS Amplify CLI configured for a Region where AWS AppSync and all other services in use are available
    (npm install -g @aws-amplify/cli)

This article uses the AWS Cloud9 integrated development environment (IDE) for building and running the application. For more information, see Getting Started with AWS Cloud9.

After you have an AWS Cloud9 environment running, use the following snippet to install these prerequisites. In your AWS Cloud9 IDE environment, open the Terminal pane and execute the following command, also shown in the following screenshot:

curl -s -L https://git.io/fjD9R | bash

Downloading and running the sample application

To download and run the sample application, perform the following steps:

1. Clone the repository and navigate to the created folder.

git clone https://github.com/aws-samples/aws-appsync-survey-tool.git
cd aws-appsync-survey-tool

As we’ll use AWS CodeCommit later on, don’t forget to set up the credential helper for git when accessing CodeCommit repositories.

2. Install the required npm modules.

npm install

3. Initialize the directory as an Amplify JavaScript app using the React framework.

amplify init

4. Provision your cloud resources based on the local setup and configured features. When asked to generate code, answer NO (answering YES overwrites the current custom files in the src/graphql folder).

amplify push

5. Run the project in AWS Cloud9.

npm start

Accessing the survey tool PWA

Only authenticated users can access the Survey Tool, so go through the process on the sign-in page to register as a new user. Once you complete the registration, sign in. There should not be any surveys for you to fill in yet.

The Survey Tool uses the add-user-to-group Amazon Cognito Lambda trigger that is part of the Amplify CLI. It runs a Lambda function on registration and performs the following:

  • Checks to see if any Groups exist in the Amazon Cognito Pool
  • Creates the default SurveyAdmins and Users groups if no groups exist
  • Adds the first user to register to the Survey Admins group
  • Adds all subsequent user registrations to the default Users group

To see the Lambda trigger for this action, in your AWS Cloud9 environment, locate and open the /amplify/backend/function/surveypwa1a7615c6PostConfirmation/src/add-to-group.js file.

Now that your new user account has SurveyAdmin privileges, log in to the Survey Tool from a desktop browser.

Choose Profile (in the top right of the page), then choose Admin Portal. The Admin Portal allows you to create new surveys, questionnaires, and questions, as well as manage users and groups and their privileges.

On the Surveys page, choose Import Sample Survey to load a sample survey. Then choose Home to find the sample survey:

Exploring the data model

The underlying data model provides the foundation for how the application interface is laid out, and how data stores are structured. Exploring it helps demonstrate how the application functions.

In your AWS Cloud9 environment, locate and open the /amplify/backend/api/surveypwa/schema.graphql file.

GraphQL defines the query language used in the application, the Type system for how data is accessed, and how data is stored in a backend system, which in this case is AWS AppSync backed by DynamoDB.

The GraphQL schema for the Survey Tool application defines five core types:

  • Survey: Defines a root level object that contains questionnaires to which users respond.
  • Questionnaire: Defines a collection of questions presented to users.
  • Question: Defines an individual question.
  • Responses: Stores responses to individual questions.
  • SurveyEntries: Groups responses when a user completes a questionnaire.

Amplify extends the GraphQL Schema Definition Language (SDL) with the GraphQL Transformer, which allow you to use directives that tell Amplify to deploy and configure AWS services for your application.

Each of the types defined in schema.graphql contains the @model directive, which tells Amplify to generate a DynamoDB table for that type. It also tells AWS AppSync to generate a data source and corresponding CRUDL logic on resolvers to allow clients to interact with the DynamoDB table.

Additionally, on the Survey, Responses, and SurveyEntries Types there is an @auth directive, which tells AWS AppSync to enforce Amazon Cognito-based authorization on actions against those types. In the case of the Survey Tool, the Survey Type allows the SurveyAdmins group in the Amazon Cognito User Pool to perform all operations on Survey objects. However, it only allows standard users to read the Survey object if their group membership matches with a group name defined in a Survey’s attributes.

type Survey
  @model
  @auth (rules: [
    {allow: groups, groups: ["SurveyAdmins"]},
    {allow: groups, groupsField: "groups", operations: [read]}
    ])
{
  id: ID!
  name: String!
  description: String!
  image: AWSURL
  preQuestionnaire: Questionnaire @connection
  mainQuestionnaire: Questionnaire @connection
  postQuestionnaire: Questionnaire @connection
  archived: Boolean
  groups: [String]!
}

How client applications interact using GraphQL

The data model defined in the schema.graphql file allows Amplify to generate the AWS backend resources required for the application, but how does the client application know how to communicate with them?

Amplify comes to the rescue here. When you run the amplify push or amplify codegen commands, Amplify uses a GraphQL feature called introspection. This feature pulls details of the AWS AppSync API you created, which generates a set of client-side code to integrate into the application.

Now, go to the src/graphql directory and find a set of JavaScript files containing the details of GraphQL API calls to use in the application. With this done automatically, import the GraphQL definition objects from these files to help ensure consistency and speed up development.

GraphQL requests made to the backend AWS AppSync API are performed over HTTPS. In REST APIs requests are structured based on URI paths and sent over a variety of HTTP methods. In contrast, GraphQL communicates with a single endpoint using only the HTTP POST method.

To abstract the complexity of creating and sending requests, and then receiving and processing responses, use the AppSync SDK. The AppSync SDK uses the Apollo Client and provides a rich set of client-side features for interacting with AWS AppSync. The key features used in the Survey Tool are:

  • ApolloProvider: The React Higher Order Component (HOC) that integrates with a React application.
  • graphql(): The core function used to execute interactions with a GraphQL backend.
  • compose(): The function used to define multiple enhancers (graphql() operations) in a single component.

The Apollo provider takes an Apollo Client client object as its only property. A client defines how Apollo interacts with a GraphQL backend, along with details of how it manages client-side caching. In your AWS Cloud9 environment, locate and open the /src/index.js file and find the following code block:

const awsAppSyncLink = createAppSyncLink({
    url: awsexports.aws_appsync_graphqlEndpoint,
    region: awsexports.aws_appsync_region,
    auth: {
        type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
        jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken()
    },
    complexObjectsCredentials: () => Auth.currentCredentials()
});

This block defines how the Apollo Client is configured to connect to the GraphQL endpoint and authenticate with Amazon Cognito.

Now, in your AWS Cloud9 environment, locate and open the /src/components/home/index.js file and find the following code block:

const Home = compose(
    graphql(gql(listSurveys), {
        options: (props) => ({
            errorPolicy: 'all',
            fetchPolicy: 'cache-and-network',
        }),
        props: (props) => {
            return {
                listSurveys: props ? props : [],
            }
        }
    })
)(HomePart)

This demonstrates how the Survey Tool calls the listSurveys GraphQL query using the graphql() and compose() features of the Apollo Client. The fetchPolicy is set to cache-and-network, which tells the Apollo Client to return previously cached results if they are available for a fast response to the user. At the same time, it performs a parallel query to the backend to validate the cached dataset. The errorPolicy parameter is set to all, which ensures errors are returned to the client so they can be interpreted and act on accordingly.

Results to the query, along with any errors and other useful metadata, are returned through the props object of the query.

The Survey Tool application also uses extended features of Apollo, such as apollo-link and apollo-link-state, to allow the application to manage both the remote and local React states. (Their use is outside the scope of this guide.)

Refining the Survey Tool

Follow these procedures to make sure that Survey Tool is reliable, fast, and engaging.

Making the Survey Tool PWA reliable

The first requirement stated in the definition of a PWA is that a PWA must be reliable: it should function for end users regardless of their network connectivity state. It should work when online, offline, and everywhere in between.

Using the ApolloProvider inherits its caching features, which allow it to not only manage a normalized cache of GraphQL query responses, but also the ability to execute mutations against the AWS AppSync API, even when offline.

This offline feature is achieved by caching a mutation operation until network connectivity is restored, or is more reliable. It returns an optimistic response in anticipation of a successful response from the AWS AppSync API. Users can interact with the PWA even if they are flying on a plane without internet access, driving through the Australian outback with intermittent and unreliable connectivity, or sitting at home with high-speed internet access.

Making the Survey Tool PWA fast

Assuming a reliable method for interacting with data sources, how do you ensure the application itself can quickly load and run while offline or in poor connectivity scenarios?

A service worker (web worker) is a modern browser function that allows web applications to offload tasks to a separate thread that persists while the browser is running (either actively or in the background). This allows tasks such as listening for asynchronous push notifications from backend infrastructure, responding to browser events, and pre-caching content without blocking the main UI thread.

The service worker included with PWAs created using the Create React App command caches all non-cross-origin content for your PWA. To validate this, use the developer tools of your browser of choice to check the cache storage.

The following screenshot shows an example of the content cached by Chrome for the Survey Tool app. The service worker has cached the HTML, CSS, and JS assets required for the PWA to function.

Making the Survey Tool PWA engaging

A key attribute of any PWA is the ability to have it install. For a mobile device, this means allowing the user to add the app to their home screen. For a desktop device, it means being able to install the app for access from the macOS Launchpad or the Windows Start Menu.

To achieve this, manually add or install the PWA on a mobile device by choosing the Share icon, then choosing Add to Home Screen.

From a desktop browser, locate the browser settings and choose Install Survey Tool.

The Survey Tool also uses a PWA feature, the manifest.json file in the root of the application, to tell a browser that it is installable. When this file is present and configured correctly, the browser fires an event that allows the application to present the user an option to install.

Integrating Amazon Pinpoint Web Analytics

To help understand the usage characteristics of the Survey Tool, use the Web Analytics feature of Amazon Pinpoint to collect data as users interact with the application.

This data is aggregated by Amazon Pinpoint and allows you to visualize usage patterns and stream the event data using Amazon Kinesis to other systems for more detailed analytics. The other systems could include Amazon S3 with Amazon Athena, or Amazon Elasticsearch Service.

Amplify simplifies the process for integrating Amazon Pinpoint Web Analytics into the application. In your AWS Cloud9 environment, locate and open the /src/index.js file.

Note the named import of the Analytics component from aws-amplify.

import { Auth, Analytics } from 'aws-amplify';

This allows you to enable and configure tracking modes at the root of the application so they are applied globally. Further down, note the Analytics.authTrack calls, which configure the three types of tracking in your application: Session, Page View, and Event.

Session tracking allows Amazon Pinpoint to collect metrics on when a user has an active session with the application. In your application, all you have to do is enable this feature with the following code:

Analytics.autoTrack('session', {
    enable: true,
    provider: 'AWSPinpoint'
});

This allows you to visualize the total number of sessions in Amazon Pinpoint, sessions per user, and sessions per device.

Page view tracking allows Amazon Pinpoint to collect metrics of which pages in the application were accessed:

Analytics.autoTrack('pageView', {
    enable: true,
    eventName: 'pageView',
    type: 'SPA',
    provider: 'AWSPinpoint',
    getUrl: () => {
        return window.location.origin + window.location.pathname;
    }
});

Finally, event tracking lets you tag elements in your pages that fire events to record data to Amazon Pinpoint. In this example application, you track click events for DOM elements that have the specified selectorPrefix:

Analytics.autoTrack('event', {
    enable: true,
    events: ['click'],
    selectorPrefix: 'data-amplify-analytics-',
    provider: 'AWSPinpoint'
});

As an example, in your AWS Cloud9 environment, locate and open the /src/components/addentry/index.js file to find a button element tagged with the selectorPrefix. This provides the hook for the Amplify Analytics module to respond to click events and send a set of attributes with the event to Amazon Pinpoint.

<Button variant="contained" color="primary" className={classes.button} onClick={handleAdd.bind(this)} 
    data-amplify-analytics-on='click' 
    data-amplify-analytics-name='click' 
    data-amplify-analytics-attrs={`addEntry:click,questionnaire:${getQuestionnaire.id}`}>
    <AddIcon />
    Add Entry
</Button>

Deploying the solution using the Amplify Console

The Amplify Console simplifies the deployment of the application, allowing you to connect to a code repository of your choice, such as AWS CodeCommit, GitHub, GitLab, or BitBucket. The frontend and backend of the application are deployed atomically in a single workflow.

To get started, you must push the application code to a repository of your own. If you choose to use CodeCommit, you can follow the CodeCommit guidance to create a new repository, and then connect your local repo to the new repository.

Now that the application is in CodeCommit, open the Amplify Console and choose Get Started under the Deploy section of the homepage.

Now, select AWS CodeCommit as your Git Provider and choose Continue.

On the Configure build settings screen, fill in the required details, as shown in the following screenshot. To grant the Amplify Console the ability to deploy and manage the Survey Tool’s backend components, select an existing Service Role value with the required IAM privileges. You could also select Create new role to have the Amplify Console step you through the role creation.

Review the Build Settings section and note that there is both a Frontend and Backend deployment section. Choose Next to continue to the Review screen, then choose Save and Deploy to complete the wizard.

After the Amplify Console has completed the Provision, Build, Deploy, Verify steps, you can access the PWA using the link provided.

Cleaning up

When you’ve finished exploring the Survey Tool and would like to remove all resources deployed into your AWS account, run the following command in the Terminal window of your AWS Cloud9 environment:

amplify delete

To remove the resources created by the Amplify Console, locate your app in the Amplify Console window, and choose Actions, Delete App.

Conclusion

This post introduced the Survey Tool sample app and explored some of the core technologies used to create a functional progressive web application, including:

  • Amplify Framework
  • AWS AppSync
  • Amazon Pinpoint
  • AWS Amplify Console

Using this example, you can see how to combine cloud-native services and modern development approaches to create engaging and useful experiences for your users.