Serverless computing is a trending technology thanks to the benefits that it provides. Serverless allows you as a developer to build applications with a backend without the need to create, manage or think about the servers. The result is that you can focus on developing your core product with increased development speed, flexibility and scalability.

In this tutorial you’ll learn how to create and use a serverless backend created with AWS Amplify in a mobile app using React Native. Amplify has a CLI toolchain for creating and managing serverless backends with support for data storage, creating and managing both GraphQL and REST apis, authentication and other features. The framework provides everything you need to develop and deploy cloud-based apps.

Goals

This tutorial will show you how to create a serverless backend with AWS Amplify and integrate it in a React Native app. We’ll first run through the required installations and setup, then we’ll create a CRUD (create, read, update, delete) Todo app powered by the AWS cloud services.

Hopefully, this article will equip you with basic knowledge of AWS Amplify, its Command Line Interface (CLI) and some of the abilities it has. After reading this article, you should be able to start exploring the system yourself.

Prerequisites

To use any AWS Amplify service and to be able to follow this tutorial, you’ll need a AWS account (it’s free). If you don’t have one, start by creating one here before you proceed.

The main focus of this tutorial is the Amplify Framework but we’ll be building a React Native application. If you’re new to React, I recommend that you check out the React Beginners Tutorial. You should also have some basic knowledge of HTML, CSS and JavsScript (including ES6 features and understanding async/await.

Configure Amplify CLI

After you’ve covered the requirements mentioned above, we can begin the installation and setup.

The Amplify CLI requires Node.js version 8.x or higher and npm version 5.x or higher. Open up your terminal and run the following commands to check your installed versions. Or install Node.js if you don’t have it installed (it will install npm as well).

node -v
npm -v

Once you have the required versions installed, we’re ready to install the Amplify CLI.

npm install -g @aws-amplify/cli

Once the the CLI is installed, we need to configure an Amplify user. In the terminal, run the command. This will take you through a couple of steps to help you with the setup.

amplify configure

The steps are:

  1. AWS administrator account - Sign in to your AWS administrator account at https://console.aws.amazon.com/ and then go back to the terminal.
  2. Specify the AWS Region - choose the region which you expect to have the most traffic. All AWS services aren’t available in all regions, so make sure the service you intend to use is available in the specific region. You can find all information at the AWS Region Table
  3. Create a new IAM user - It will help you create a new AWS IAM user (Identity and Access Management), which is another AWS web service that secures your AWS account by controlling who can log in and access the resources. You can read more about IAM users here.

Enter a username for your new IAM account and hit Enter. The console will once again open a browser window where you have the complete to setup of the IAM user. Here you can modify the username and choose the access type. Make sure Programmatic access is selected as it allows you to control your AWS resources from the CLI. Then click Next: Permissions

iam-user-step1

Then we need to give the user a policy which controls the users permissions. Administrator Access has been selected by default which makes the user administrator with full access to the AWS services. We’ll leave it as it is and click Next: Tags.

iam-user-step2

We’ll leave the tags blank and proceed to the next page, click on Next: Review. Go through the information to make sure it is created by the instructions above, then click Create User to finish the process.

You’ll be guided to a new page with the credentials of the new user you created. Do not close the page yet. We’ll need to add both the Access Key and the Secret access key back in the terminal. I recommend downloading the .csv file and storing this in a secure place.

iam-user-finished

Go back to the terminal and hit Enter. Add the Access Key and then the Secret access key. Lastly, you’ll be asked for a profile name on your local machine. I recommend entering the username of the IAM user or the project name. Hit Enter once again to complete the process.

The Todo App

Since the purpose of this tutorial is to help you get started with AWS Amplify, we won’t be creating the app from scratch. Instead we’ll clone a React Native Todo app from GitHub (Source). In your terminal, navigate to where you want your project to live. To clone the project from GitHub, run the below.

git clone https://github.com/boorje/react-native-todo-app.git

This will create a local clone of the project and we can navigate inside the project, install the dependencies and then run the project.

cd react-native-todo-app
npm install && expo start

When the project is setup, take a minute or two to play around with the app and familiarize yourself with the code. After this, we’re ready to setup the serverless backend inside the app.

Initialize AWS Amplify

Initializing Amplify within the project is easy since the CLI takes care of most of the setup for us. However, you’ll have to be prepared to answer a few more questions. Make sure you’re at the root directory of the project and run the command.

amplify init

Below, you’ll see the configuration I used to initialize Amplify. Make sure to adapt the project name and the editor you’re using.

amplify-step

Next, you’ll be asked if you want to use an AWS profile. This is referring to the IAM user that we created earlier. Choose Y, hit Enter and then select your AWS profile. This will create the project in the cloud. When it’s finished loading you might notice some new files inside the project.

The CLI adds a /amplify folder and a aws-exports.js file with the initial project configuration. Without going too deep into the configuration process and setup, it’s worth mentioning that the /amplify folder is split up into two main directories.

  • /backend - The folder contains the latest changes made to the backend resources on your local machine, to be pushed to the cloud.
  • /#current-cloud-backend - Contains the specs for the backend resources in the cloud from the latest synchronization made.

When making changes to your app on your local machine, you’ll need to manually update them in the cloud. You can do this with special commands from the Amplify toolchain. Running amplify push will push your local development changes to the cloud.

The aws-exports.js file stores details about the regions and cloud services used in the app. Whenever you run amplify push, this file will be automatically created also overriding any changes you make directly to the file.

Add GraphQL API

With the Amplify toolchain we can easily create and connect to a serverless backend with cloud storing capabilities through our terminal. Amplify offer different categories, which are groups of resources which work together to add a specific feature to the app. In this section we’ll be using the API category.

The API category allows us to make HTTP requests to REST and GraphQL endpoints. In this tutorial, we’ll be creating a GraphQL endpoint using AWS AppSync. AWS AppSync helps us build data-driven applications with support for both real-time and offline capabilities. To be more specific, with AWS AppSync we can create an API which securely accesses (and combines) data from one or multiple data sources such as databases, APIs or other backend systems.

Amplify offers two different options to use AppSync, the lightweight AWS GraphQL client and the AWS AppSync SDK which offers more features like offline support. For this tutorial, the GraphQL client will do the work.

Configuration With CLI

Using the CLI, we can configure the AppSync API and generate the configuration files and code to integrate with it. Go to your terminal and make sure you’re at the root of the project. The toolchain will once again handle most of the process for us, all we need to do is to answer some questions.

amplify add api

amplify-add-api

Most of the questions are straightforward but there are some things to note.

  • We start by choosing GraphQL.
  • For this tutorial we’re selecting API key (which expires after 180 days) as the authorization type for the API. This is only recommended for development. In a real production ready application, you’ll probably limit your AWS resources to authenticated users only. In this case, you’ll go with Amazon Cognito User Pool. You can always update the API with the command amplify update api.
  • We’re not configuring any advanced settings for the API.
  • We use the CLI to help us create a schema for our todos. We’re not going to edit it now, we’ll see the created schema soon.

Now we can run amplify status in the terminal to see what resources have been added or modified. In this case we can see that the affected Category is API, which the Resource name set to the name of our created API. The Operation specifies what action has been performed and the Provider plugin specifies that it should be published to the cloud.

amplify-status

Now we need to push the changes to the cloud.

amplify push

When asked if you want to continue, make sure it’s the correct operation on the correct Category and then select Y to proceed. You’ll then be asked if you want to generate code for the new API. The focus in this tutorial is not on GraphQL but instead the setup and usage of the API. Choose Y to generate the code which will be followed by some more questions:

  • target language - choose javascript. If you’re planning on using TypeScript or flow you can select this here instead.
  • file name pattern - choose the default pattern src/graphql/**/*.js. This folder structure will contain the GraphQL schema and related operations as JavaScript files. We’ll be using these files to connect to the API later on.
  • generate GraphQL operations - choose Yes. This will generate all the GraphQL operations (queries, mutations and subscriptions) for the schema.
  • maximum statement depth - choose the default 2.

This will update your resources in the cloud, add a /graphql folder inside /src and update the aws-exports.js file with the information for the API.

Folder Structure

We have now successfully setup a GraphQL API in our project. Before we start building the app and integrating with the API, we’ll take a moment to review what actually has happened to the client-side of our project. If you haven’t already, open up the project in your code editor. We will take a look at to folders: /amplify/backend and /src/graphql.

/amplify/backend

When we ran amplify add api and pushed this to the cloud, the CLI took care of some things for us. From the root of the project, go to the /amplify folder and open up the /backend folder. Remember this is were our local development changes reside.

After adding the API, the CLI has added the file backend-config.json with the information that we entered during the setup in the terminal and the /api folder. Inside the folder you’ll find another folder with the name of your API. Inside this folder there’s a couple of files and folders. Don’t be overwhelmed, for now we’ll focus on the file schema.graphql.

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

Inside is the model that was created when we chose to have a guided schema creation when we added the GraphQL API. If you’re familiar with GraphQL you can skip this section, otherwise here’s an brief overview of GraphGL models.

A type is a kind of object which you can fetch from a service, and the fields it has. In our case, we define the Todo type with three fields: id, name and description. Object types that are annotated with @model are stored in Amazon DynamoDB, which is a NoSQL database provided by AWS.

Every type in a database generates a unique identifier which is used when making CRUD operations. In our case, we have an id field which is auto-populated by Amplify during creation. The id field has a value of ID which is a built-in GraphQL type known as a scalar type (String is another built-in scalar type). The ! indicates that the field is required. I highly recommend learning GraphQL (here) if you plan to use this in your future projects.

/src/graphql

Inside this folder, you’ll find JavaScript files for the queries, mutations, subscription and a json format of the schema. Inside each file (excluding schema.json) there’s a few functions which respectively perform specific GraphQL actions. We won’t be going through these files now, but we’ll be using them in the next section.

Using The API

We’re now ready to start connecting to the API from the app itself. The first step is to install Amplify to the application. Run the following command in our terminal.

@aws-amplify/api

When we pushed the API to the cloud, we were asked if the CLI should generate GraphQL operations for us. If you entered Yes (which the following section assumes you did), you should have a /graphql folder with one file for each operation: queries, mutations and subscriptions. These generated files contain the code for all possible GraphQL statements.

Create a new file api.js inside /src. Inside this file, we’ll be creating helper functions for connecting with the GraphQL operations. Therefore we need to import the queries and mutations from the /graphql folder. Last, we also need configure Amplify using the aws-exports.js.

import API, { graphqlOperation } from "@aws-amplify/api";

// operations
import * as queries from "./graphql/queries";
import * as mutations from "./graphql/mutations";

// configure amplify
import config from "../aws-exports";
API.configure(config);

Before we actually use the API, we’ll add a state type inside App.js to handle any errors.

const [hasError, setError] = useState(false);

Then inside the render function, we’ll add a text message (with some basic inline styling) which is displayed when we have an error.

<SafeAreaView>
  {hasError && (
    <Text style={{ alignSelf: "center", margin: 10, color: "red" }}>
      Something went wrong. Please try again.
    </Text>
  )}
  ...
</SafeAreaView>

This is a very simple UI component with very little information provided to the user. However, we can now catch any errors from the API and call the setter method setError to update the state.

Fetching Todos

We can now write a function which returns all our todos from the cloud. We’ll create a new function listTodos which queries for all the todos and returns the result. To use the GraphQL statements, we’ll use two functions: API.graphql and graphqlOperation.

export const listTodos = async () => {
  const { data } = await API.graphql(graphqlOperation(queries.listTodos));
  return data.listTodos.items;
};

We can now import this function inside the App.js file. We’ll be creating more functions soon, so we’ll import all functions as api.

// api
import * as api from "./src/api";

We want to load all the todos from the cloud and add them to the state of our application. We’ll create a new function fetchTodos which calls the api.listTodos function we just created and then updates the todos with the returning result.

fetchTodos = async () => {
  try {
    const todos = await api.listTodos();
    setTodos(todos);
  } catch (error) {
    setError(true);
  }
};

Now we can call this method from inside the effect hook.

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

There’s two things to mention here.

  1. We haven’t created any todos in the cloud yet so it should be empty.
  2. The Todo model which was auto-created for us by the CLI uses the fields: id, name, description. Inside our app, the todo object has different fields: id and text which will cause some issues moving forward. Let’s address this right away by updating the GraphQl schema.

Updating the API

In order to update the model, we need to change our GraphQL schema. Starting from the root of the project, go to amplify/backend/api/YOURPROJECTNAME/schema.graphql. Inside the file, we’ll change the schema so it has two fields: id and text .

type Todo @model {
  id: ID!
  text: String!
}

We need to push these changes to the cloud by running amplify push. Just like the first time we added the API, you’ll be asked if you want to update the code for the GraphQL API and the statements. Choose Yes for both.

Adding a Todo

Adding a todo is quite similar to what we previously did. Inside the api.js file, create a new function createTodo.

export const createTodo = async todo => {
  const { data } = await API.graphql(
    graphqlOperation(mutations.createTodo, {
      input: { text: todo },
    })
  );
  return data.createTodo;
};

The difference here is that we pass two arguments into the graphqlOperation method. The first is the imported GraphQL statement (in this case a mutation) and the second is the input for the todo that we want to create.

Then we need to call this function from App.js when we add a new todo in order to actually create it inside the cloud. Inside App.js we’ll call the api.createTodo method passing in the text state as a parameter.

createTodo = async () => {
  try {
    const createdTodo = await api.createTodo(text);
    setTodos([...todos, { id: createdTodo.id, text }]);
    setText("");
  } catch (error) {
    setError(true);
  }
};

In the above code, you might have noticed that we’re not using the shortid to generate an id anymore. Instead, we’re using the id property of the returned object from the createTodo method.

If you’re interested in what the GraphQL statements return, you can find this inside /src/graphql/ files. If you open the mutations.js file inside the folder, you can see that the id and text is returned of the newly created todo.

export const createTodo = `mutation CreateTodo($input: CreateTodoInput!) {
  createTodo(input: $input) {
    id    text  }
}
`;

Updating a Todo

Once again, the process for updating a todo is similar to the other operations. We’ll add a new function updateTodo inside the api.js file.

export const updateTodo = async input => {
  const { data } = await API.graphql(
    graphqlOperation(mutations.updateTodo, { input })
  );
  return data.updateTodo;
};

This time we’re using the mutations.updateTodo GraphQL statement and passing through an object as the input. Inside App.js, we call the api.updateTodo method passing in an object as a parameter. The object should contain the values of the todo which is being updated, i.e. the value of the states currentTodo and text.

updateTodo = async () => {
  try {
    await api.updateTodo({ id: currentTodo, text });
    setTodos(
      todos.map(todo =>
        todo.id === currentTodo ? { id: currentTodo, text } : todo
      )
    );
    setText("");
    setEditing(false);
  } catch (error) {
    setError(true);
  }
};

Deleting a Todo

The last operation we’ll be adding, is deleting a todo. We’ll start by creating a new function inside api.js which calls the graphqlOperation method with the deleteTodo mutation and the id of the todo to delete.

export const deleteTodo = async id => {
  const { data } = await API.graphql(
    graphqlOperation(mutations.deleteTodo, { input: { id } })
  );
  return data.deleteTodo;
};

Next, we’ll add this method inside the App.js.

deleteTodo = async id => {
  try {
    await api.deleteTodo(id);
    setTodos(todos.filter(todo => todo.id !== id));
  } catch (error) {
    setError(true);
  }
};

We have now connected the CRUD operations with the GraphQL API. If you haven’t already, you can start the app to see the result and play around with the app.

expo start

finished-app

The AWS Resources

Adding an GraphQL API is very easy to do thanks to the Amplify toolchain. We just run a line in the terminal, answer some questions and then we have a configured API setup for us. Let’s take a minute to look at the AWS resources that have been created through the CLI.

AppSync

When we created our GraphQL API, the toolchain create a new API for us in AppSync. If you go to this service, you should be able to see and open your API. Inside here, you can see relevant information including the actual schema and the data sources it uses. If you go the Queries tab, you can play around with the API to create your own GraphQL operations. This is a great way to start learning and understanding how you can interact with the API.

aws-appsync

In the Data Sources tab in the AppSync console, you should see a row with the name TodoTable and type AMAZON_DYNAMODB.

DynamoDB

DynamoDB is used as the database to store the todos that we create. If you sign in to your console, you will see the dashboard with lots of information. We’ll focus on the Tables tab which you’ll find in the menu to the left of the page. Inside, you should be able to see the TodoTable which is referenced in AppSync.

Click on the table to open it. Inside, you’ll have another menu in the top with different options. If you have created todos from the React Native app you can see these in the Items tab.

aws-dynamodb

Wrapping up

You should by know have gained a basic understanding of what AWS Amplify is and how you can use it in your app. In the tutorial we created a Todo app in React Native with a serverless backend. If you got lost somewhere in the process, you can find the source code below. You should use the source code as a guideline rather than cloning the project, since it doesn’t have the required Amplify files. If you’re interested to learn how to deploy a React Native app, check out the previous post which shows how to deploy a React Native app to TestFlight using Fastlane.

I hope that the tutorial has given you the confidence to start exploring Amplify yourself. I will be writing more tutorials covering AWS Amplify (including how to add login and social login) so be sure to stay updated.