Build and Deploy a Serverless GraphQL React App Using AWS Amplify

mokkapps

Michael Hoffmann

Posted on May 7, 2021

Build and Deploy a Serverless GraphQL React App Using AWS Amplify

Recently I recognized that some SaaS (Software as a Service) products use AWS Amplify which helps them to build serverless full-stack applications. I think serverless computing will be the future of apps and software. Therefore, I wanted to gather some hands-on experience, and I built a serverless application using AWS Amplify that uses React as frontend framework and GraphQL as backend API.

In this article, I want to guide you through the process how to build and deploy such an Amplify application.

Table of Contents

Set up Amplify

AWS Amplify describes itself as:

Fastest, easiest way to build mobile and web apps that scale

Amplify provides tools and services to build scalable full-stack applications powered by AWS (Amazon Web Services). With Amplify configuring backends and deploying static web apps is easy. It supports web frameworks like Angular, React, Vue, JavaScript, Next.js, and mobile platforms including iOS, Android, React Native, Ionic, and Flutter.

You’ll need to create an AWS account to follow the following steps. No worries, after signing up you have access to the AWS Free Tier which does not include any upfront charges or term commitments.

The next step is to install the Amplify Command Line Interface (CLI). In my case I used cURL on macOS:

curl -sL https://aws-amplify.github.io/amplify-cli/install | bash && $SHELL
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can watch this video to learn how to install and configure the Amplify CLI.

Now we can configure Amplify using the CLI

amplify configure
Enter fullscreen mode Exit fullscreen mode

which will ask us to sign in to AWS Console. Once we’re signed in, Amplify CLI will ask us to create an AWS IAM user:

Specify the AWS Region
? region: # Your preferred region
Specify the username of the new IAM user:
? user name: # User name for Amplify IAM user
Complete the user creation using the AWS console
Enter fullscreen mode Exit fullscreen mode

We’ll be redirected to IAM where we need to finish the wizard and create a user with AdministratorAccess in our account to provision AWS resources. Once the user is created, Amplify CLI will ask us to provide the accessKeyId and secretAccessKey to connect Amplify CLI with our created IAM user:

Enter the access key of the newly created user:
? accessKeyId: # YOUR_ACCESS_KEY_ID
? secretAccessKey: # YOUR_SECRET_ACCESS_KEY
This would update/create the AWS Profile in your local machine
? Profile Name: # (default)

Successfully set up the new user.
Enter fullscreen mode Exit fullscreen mode

Set up full-stack Amplify project

At this point, we are ready to set up our full-stack project using a React application in the frontend and GraphQL as backend API. We’ll build a Todo CRUD (create, read, update, delete) application that uses this architecture:

Amplify demo architecture

The complete source code of this demo is available at GitHub.

Create React frontend

Let’s start by creating a new React app using create-react-app. From our projects directory we run the following commands to create our new React app in a directory called amplify-react-graphql-demo and to navigate into that new directory:

npx create-react-app amplify-react-graphql-demo
cd amplify-react-graphql-demo
Enter fullscreen mode Exit fullscreen mode

To start our React app we can run

npm start
Enter fullscreen mode Exit fullscreen mode

which will start the development server at http://localhost:3000.

Initialize Amplify

Now it’s time to initialize Amplify in our project. From the root of the project we run

amplify init
Enter fullscreen mode Exit fullscreen mode

which will prompt some information about the app:

▶ amplify init
? Enter a name for the project amplifyreactdemo
The following configuration will be applied:

Project information
| Name: amplifyreactdemo
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: react
| Source Directory Path: src
| Distribution Directory Path: build
| Build Command: npm run-script build
| Start Command: npm run-script start

? Initialize the project with the above configuration? Yes
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile
? Please choose the profile you want to use: default
Enter fullscreen mode Exit fullscreen mode

When our new Amplify project is initialized, the CLI:

  • created a file called aws-exports.js in the src directory that holds all the configuration for the services we create with Amplify
  • created a top-level directory called amplify that contains our backend definition
  • modified the .gitignore file and adds some generated files to the ignore list

Additionally, a new cloud project is created in the AWS Amplify Console that can be accessed by running amplify console. Amplify Console provides two main services: hosting and the Admin UI. More information can be found here.

The next step is to install some Amplify libraries:

npm install aws-amplify @aws-amplify/ui-react typescript
Enter fullscreen mode Exit fullscreen mode
  • aws-amplify: the main library for working with Amplify in your apps
  • @aws-amplify/ui-react: includes React specific UI components
  • typescript: we will use TypeScript in some parts of this demo

Next, we need to configure Amplify on the client. Therefore, we need to add the following code below the last import in src/index.js :

import Amplify from 'aws-amplify';
import awsExports from './aws-exports';
Amplify.configure(awsExports);
Enter fullscreen mode Exit fullscreen mode

At this point wee have a running React frontend application, Amplify is configured, and we can now add our GraphQL API.

Create GraphQL API

We will now create a backend that provides a GraphQL API using AWS AppSync (a managed GraphQL service) that uses Amazon DynamoDB (a NoSQL database).

To add a new API we need to run the following command in our project’s root folder:

▶ amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: demoapi
? Choose the default authorization type for the API: API key
? Enter a description for the API key:
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API: No, I am done.
? Do you have an annotated GraphQL schema? No
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)
Enter fullscreen mode Exit fullscreen mode

After the process finished successfully we can inspect the GraphQL schema at amplify/backend/api/demoapi/schema.graphql:

type Todo @model {
  id: ID!
  name: String!
  description: String
}
Enter fullscreen mode Exit fullscreen mode

The generated Todo type is annotated with a @model directive that is part of the GraphQL transform library of Amplify. The library contains multiple directives which can be used for authentication, to define data models, and more. Adding the @model directive will create a database table for this type (in our example a Todo table), the CRUD (create, read, update, delete) schema, and the corresponding GraphQL resolvers.

Now it’s time to deploy our backend:

▶ amplify push
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name | Operation | Provider plugin |
| -------- | ------------- | --------- | ----------------- |
| Api | demoapi | Create | awscloudformation |
? Are you sure you want to continue? Yes
? Do you want to generate code for your newly created GraphQL API: Yes
? Choose the code generation language target: typescript
? Enter the file name pattern of graphql queries, mutations and subscriptions: src/graphql/**/*.ts
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions: Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2
? Enter the file name for the generated code: src/API.ts
Enter fullscreen mode Exit fullscreen mode

After it is finished successfully our GraphQL API is deployed and we can interact with it. To see and interact with the GraphQL API in the AppSync console at any time we can run:

amplify console api
Enter fullscreen mode Exit fullscreen mode

AppSync GraphQL API Console

Alternatively, we can run this command

amplify console api
Enter fullscreen mode Exit fullscreen mode

to view the entire app in the Amplify console.

Connect frontend to API

The GraphQL mutations, queries and subscriptions are available at src/graphql. To be able to interact with them we can use the generated src/API.ts file. So we need extend App.js to be able to create, edit and delete Todos via our GraphQL API:

import React, { useEffect, useState } from 'react';
import { API, graphqlOperation } from '@aws-amplify/api';
import { listTodos } from './graphql/queries';
import { createTodo, deleteTodo, updateTodo } from './graphql/mutations';
import TodoList from './components/TodoList';
import CreateTodo from './components/CreateTodo';

const initialState = { name: '', description: '' };

function App() {
  const [formState, setFormState] = useState(initialState);
  const [todos, setTodos] = useState([]);
  const [apiError, setApiError] = useState();
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    fetchTodos();
  }, []);

  function setInput(key, value) {
    setFormState({ ...formState, [key]: value });
  }

  async function fetchTodos() {
    setIsLoading(true);
    try {
      const todoData = await API.graphql(graphqlOperation(listTodos)); const todos = todoData.data.listTodos.items;
      setTodos(todos);
      setApiError(null);
    } catch (error) {
      console.error('Failed fetching todos:', error);
      setApiError(error);
    } finally {
      setIsLoading(false);
    }
  }

  async function addTodo() {
    try {
      if (!formState.name || !formState.description) {
        return;
      }
      const todo = { ...formState };
      setTodos([...todos, todo]);
      setFormState(initialState);
      await API.graphql(graphqlOperation(createTodo, { input: todo })); setApiError(null);
    } catch (error) {
      console.error('Failed creating todo:', error);
      setApiError(error);
    }
  }

  async function removeTodo(id) {
    try {
      await API.graphql(graphqlOperation(deleteTodo, { input: { id } })); setTodos(todos.filter(todo => todo.id !== id));
      setApiError(null);
    } catch (error) {
      console.error('Failed deleting todo:', error);
      setApiError(error);
    }
  }

  async function onItemUpdate(todo) {
    try {
      await API.graphql( graphqlOperation(updateTodo, { input: { name: todo.name, description: todo.description, id: todo.id, }, }) ); setApiError(null);
    } catch (error) {
      console.error('Failed updating todo:', error);
      setApiError(error);
    }
  }

  const errorMessage = apiError && (
    <p style={styles.errorText}>
      {apiError.errors.map(error => (
        <p>{error.message}</p>
      ))}
    </p>
  );

  if (isLoading) {
    return 'Loading...';
  }

  return (
    <div style={styles.container}>
      <h1 style={styles.heading}>Amplify React & GraphQL Todos</h1>
      {errorMessage}
      <div style={styles.grid}>
        <TodoList
          todos={todos}
          onRemoveTodo={removeTodo}
          onItemUpdate={onItemUpdate}
        />
        <CreateTodo
          description={formState.description}
          name={formState.name}
          onCreate={addTodo}
          onDescriptionChange={setInput}
          onNameChange={setInput}
        />
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

The full source code of this demo is available at GitHub.

The application should show a list of available Todos which can be edited or deleted. Additionally, we have the possibility to create new Todos:

Amplify Frontend Running Locally

Add authentication

Amplify uses Amazon Cognito as the main authentication provider. We’ll use it to add authentication to our application by adding a login that requires a password and username.

To add authentication we need to run

▶ amplify add auth
Using service: Cognito, provided by: awscloudformation
 The current configured provider is Amazon Cognito.

 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections.
 How do you want users to be able to sign in? Username
 Do you want to configure advanced settings? No, I am done.
Enter fullscreen mode Exit fullscreen mode

and deploy our service by running

amplify push
Enter fullscreen mode Exit fullscreen mode

Now we can add the login UI to our frontend. The login flow can easily be handled by using the withAuthenticator wrapper from the @aws-amplify/ui-react package. We just need to adjust our App.js and import withAuthenticator:

import { withAuthenticator } from '@aws-amplify/ui-react';
Enter fullscreen mode Exit fullscreen mode

Now we need to wrap the main component with the withAuthenticator wrapper:

export default withAuthenticator(App);
Enter fullscreen mode Exit fullscreen mode

Running npm start will now start the app with an authentication flow allowing users to sign up and sign in:

Amplify Login

Deploy and host app

Finally, we want to deploy our app which can be either done manually or via automatic continuous deployment. In this demo I want to deploy it manually and host it as static web app. If you want to use continuous deployment instead, please check out this official guide.

First, we need to add hosting:

▶ amplify add hosting
? Select the plugin module to execute: Hosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)
? Choose a type: Manual deployment
Enter fullscreen mode Exit fullscreen mode

and then we are ready to publish our app:

amplify publish
Enter fullscreen mode Exit fullscreen mode

After publishing, we can see the app URL where our application is hosted on an `amplifyapp.com domain in our terminal.

What’s next

Amplify provides also a way to run your API locally, check out this tutorial.

Here are some cool things that you can additionally add to your Amplify application:

Take a look at the official Amplify docs for further information about the framework.

Conclusion

In this article I showed you that building and deploying a full-stack serverless application using AWS Amplify requires a minimum amount of work. Without using such a framework it would be much harder and this way you can focus more on the end product instead of what is happening inside.

If you liked this article, follow me on Twitter to get notified about new blog posts and more content from me.

💖 💪 🙅 🚩
mokkapps
Michael Hoffmann

Posted on May 7, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related