7 minutes read

POSTED Oct, 2020 dot IN Serverless

How to build an application in minutes with AWS Amplify

Serkan Özal

Written by Serkan Özal


Founder and CTO of Thundra

 X

Serverless haters usually come up with the argument of “there are still servers behind”. Although they are categorically right about the “secret” servers, what serverless implies is not about the existence of servers but harmony of developing applications without thinking about servers behind. It can be considered as how we perceive wireless, we don’t discuss if there are wires behind but instead enjoy the comfort of wireless, right. Same analogy perfectly applies to serverless. In this blog post, we’ll go over one of the coolest and also the easiest way of developing serverless applications with AWS Amplify.

Enter: AWS Amplify

A typical web application consists of client-side and server-side components. AWS Amplify is a JavaScript library that streamlines serverless application development on cloud. It abstracts all the serverfull components away and let developers focus on the business logic. With its pre-built UI components in React, Vue, AngularJS and more, it lets developers tying up the business logic with good looking applications.

Components of AWS Amplify

AWS Amplify components can be categorized into three groups: libraries, UI, and CLI ToolChain. We’ll dive into the details of each now.

The Library Component

The library component is where you build your application by adding new integrations and interactions with AWS cloud services. Developers are able to add secure authentication, data store, file storage, serverless APIs, analytics, push notification, AR/VR, and other features to their app.

UI Components

AWS Amplify provides you with pre-built UI components modeled around cloud workflows in your application, such as the authentication higher-order component — which we’ll discuss later in this article.

CLI Toolchain

As your application grows, you might need to add more cloud services. The CLI component of AWS Amplify lets developers make changes to integrated AWS services on the command line.

AWS Amplify Features

Let’s go over some of the key features before we dive into the real example of playing with some of those features.

Bootstrap Serverless Application

Using Amplify CLI, you can bootstrap client code with fully-fledged serverless back-end. You can also add the UI components that are completely customizable.

Deploy and Host Applications

Using AWS Amplify Console, developers are able host their static web applications. Once you connect your repository over Amplify console, the changes are automatically reflected to the application in seconds. The deployment method depends on the build settings you’ve configured for your app.

Building a Full Stack To Do App with AWS Amplify

A to do app is always the first choice when you want to make your hands dirty with a new technology. In this blog, we will build a to-do application that lets you add tasks, see the tasks you added, and of course, add authentication using AWS Amplify.

Here’s the high-level architecture showing what the end product will look like:

High-level architecture

Figure 1: High-level architecture

Setting up the Project

To set up a project with AWS Amplify, you’ll need to have an AWS account. Now let’s get started.

Step 1: Install the Amplify CLIspan

This library will be used to interact with cloud services (CLI Toolchain).

npm i -g @aws-amplify/cli

Step 2: Configure AWS Amplify.

This requires you to sign in and then takes you through questions to set up Amplify for your project.

amplify configure

After signing in, you’ll be asked to create an IAM user as the user who has access to your AWS account.

To create an IAM user, answer the questions about AWS Region and IAM user.

Next, you’ll be taken to the IAM Management Console to set up user access. Here’s the user creation flow:

User Creation Flow

Figure 2: User creation flow

Click “Next” and select “AdministratorAccess” if it’s not selected by default, then continue until you see the “Create user” button.

User Creation Preview Page

Figure 3: User creation preview page

Click the “Create user” button to create an Amplify user.

The IAM user is created.

Do not close the page. Head back to your terminal, click Enter and provide the accessKeyId and secretAccessKey of the newly created user in the terminal.

Step 3: Bootstrap a React application.

For the React project use create-react-app, a well-known library for kick-starting React applications.

To bootstrap the React app, run the command:

 npx create-react-app todo-amplify

With the client code generated, our next step is to initialize the back end.

Initializing the Back End

You need to add backend components such as a database and also authentication to your application. You can use AWS Cognito to authenticate users, and GraphQL endpoints to interact with the DB. Now we’ll use the AWS Amplify CLI to initialize these components to support our app:

cd todo-amplify
amplify init

You'll be presented with the following questions (the answers selected for this exercise are shown in brackets):


Enter a name for the project (todo-amplify)
Enter a name for the environment (dev)
Choose your default editor (VS Code)
Choose the type of app that you're building (javascript)
What JavaScript framework are you using (react)
Source directory path (src)
Distribution directory path (build)
Build command (npm build)
Start command (npm start)
Do you want to use an AWS profile (Y)

At this point, you should have a directory called amplify created at the root folder. This directory holds all the information about the back-end service created. A file called aws-exports.js is also created. This holds the information that will enable you to connect from the client to the back.

In addition to this, the project is now connected to Amplify Console. You can access the console by running the command amplify console from the terminal.

Setting up the Front-end App

It’s time to connect the backend app with our frontend app we previously initiated in React. AWS Amplify provides a variety of UI components for different frameworks. You can install an AWS Amplify UI component for React, our choice of framework for this tutorial, with this command:

npm install aws-amplify @aws-amplify/ui-react --save

The core library for interacting with AWS services in applications is aws-amplify . The CLI only creates back-end services. This library controls how the application connects to the back end and triggers actions.

In addition, @aws-amplify/ui-react provides React components that connect easily to the back end.

In src/index.js (the root of the React project), import aws-exports and Amplify library:

import React from 'react';
import logo from './logo.svg';
import './App.css';

import Amplify from "aws-amplify";
import awsExports from "./aws-exports";
Amplify.configure(awsExports);

function App() {
 return (
   <div className="App">
     <header className="App-header">
       <img src={logo} className="App-logo" alt="logo" />
       <p>
         Edit <code>src/App.js</code> and save to reload.
       </p>
       <a
         className="App-link"
         href="https://reactjs.org"
         target="_blank"
         rel="noopener noreferrer"
       >
         Learn React
       </a>
     </header>
   </div>
 );
}
export default App;

The aws-exports.js holds the information required to connect and interact with the back-end service.

Creating a GraphQL API

Now, we need to build the part of the application where our application use to play with data. We can create either a REST or a GraphQL API for this purpose but we’ll go with the latter because it is very declarative and lets you fetch only relevant data.

To add an API, use the following command:

 amplify add api 

Select GraphQL as your API service.

For API name and description, use todo-api and choose a description of what the API manages.

You can answer every other question as you choose. If you’re unsure of what to do, the defaults are fine.

Once you’ve answered all the questions, a GraphQL schema is created in amplify/backend/api/todo-api/schema.graphql:

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

If you’re wondering how it knew we were creating a to do app (in type: Todo), the answer is that it doesn’t. This is the default schema Amplify provides. By default also, this API has already been configured for creating, reading, updating, and deleting (CRUD) to dos.

For your to do list, you won’t need a description. Instead, we’ll need a done property to denote the to do’s status. Here’s the updated schema:

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

Now that the API is created, we can deploy it to the cloud.

From your terminal, run the following command:

 amplify push 

Connecting the Front End to API

To do this, we’ll use the methods exposed by GraphQL. In the src directory, there’s an auto-generated GraphQL directory with files that hold different methods.

To view all to dos and add a to do, do the following in src/App.js:

import React, { useState, useEffect } from "react";
import "./App.css";
import { withAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";
import { createTodo, updateTodo } from "./graphql/mutations";
import { listTodos } from "./graphql/queries";
import Amplify, { API, graphqlOperation } from "aws-amplify";
import awsExports from "./aws-exports";

Amplify.configure(awsExports);

function App() {
 const [allTodos, setAlltodos] = useState(null);

 useEffect(() => {
   (async () => {
     const todos = await API.graphql(graphqlOperation(listTodos));
     setAlltodos(todos.data.listTodos.items);
   })();
 }, []);

 const [name, setTodoName] = useState("");

 const changeTodoName = (e) => {
   setTodoName(e.target.value);
 };

 const submitAddTodo = async (e) => {
   e.preventDefault();
   if (name === "") return alert("Input field cannot be empty");
   const todo = { name, done: false };
   await API.graphql(graphqlOperation(createTodo, { input: todo }));
   allTodos === null ? setAlltodos([todo]) : setAlltodos([todo, ...allTodos]);
 };

 const toggleTodo = async (id) => {
   const todo = allTodos.find(({ id: _id }) => _id === id);
   let newTodo = { id, name: todo.name };
   newTodo.done = todo.done ? false : true;
   await API.graphql(graphqlOperation(updateTodo, { input: newTodo }));
 };

 return (
   <div className="App">
     <div className="heading">
       <h1>Amplify Todo</h1>
       <div className="sign-out">
         <AmplifySignOut />
       </div>
     </div>
     <form className="add-todo-form" onSubmit={submitAddTodo}>
       <input
         placeholder="Add Todo"
         onChange={changeTodoName}
       />
       <button type="submit">+</button>
     </form>
     {allTodos === null ? (
       <p>Loading Todos...</p>
     ) : allTodos.length === 0 ? (
       <p>No Todo available</p>
     ) : (
       <div className="todos">
         {allTodos.reverse().map(({ id, name, done },i) => (
           <div className="todo-block" key={i}>
             <input
               onClick={() => toggleTodo(id)}
               type="checkbox"
               id={id}
               value={id}
               key={i}
               defaultChecked={done}
             />
             <label htmlFor={id}>{name}</label>
           </div>
         ))}
       </div>
     )}
   </div>
 );
}

export default withAuthenticator(App);

In the useEffect hook, the API to get all to dos is called, which at first would be empty.

Then the name state is handled and on submit, the createTodo API is called. It receives an input of an object with name and done (with a value of false) properties. Remember the schema demands id (automatically generated), name, and done, so those are what must be submitted when you click the “Submit” button.

When there is a successful response from the createTodo method, instead of refreshing the page, you only update the local state of the todos array.

When a to do is checked, it’s updated in the back end with a done property of true. If it’s unchecked, then it’s false.

The AmplifySignOut component is exposed from Amplify, and it is used to trigger a sign-out after sign-in. The sign-in setup is shown in the next section (Authentication).

Here’s our application running on localhost:3000 when npm start is run:

Todo app

Figure 4: Todo app

Next is authentication.

Authenticating Users

To create the authentication service, run the following:

amplify add auth

After answering the questions, run the following:

 amplify push

amplify push syncs the local updates to the cloud service.

Now, the authentication service is ready. Instead of connecting to the back end manually through the authentication APIs that AWS Amplify provides, you can use the Amplify UI components. For our use case, you would use the withAuthenticator higher-order component.

In src/App.js, add the following:

import { withAuthenticator } from '@aws-amplify/ui-react'
.....
export default withAuthenticator(App)

When you head over to localhost:3000, you’ll get something similar to this:

Authentication page

Figure 5: Authentication page

Amplify will help you manage registration, sign-in, and user authentication for every operation. In this tutorial you can find the full source code used.

Deploying the App

Amplify Console allows you to easily deploy and host applications. To host the app, run the following:

 amplify add hosting

Next, run this:

 amplify add publish

Your app is now online and you’ll get a public URL on your terminal.

Remember that you can also view your app directly by running the command amplify console.

If you’d like to delete all the resources or clean up your cloud resources, run the following command:

 amplify delete  

Wrapping up

We built a full-stack application with authentication, front-end, API with a really minimum amount of work. Remember how hard it is with all of the complexities the days before serverless and AWS Amplify. With this technology, the only thing you should be worried about is how to provide the best value for your customers and beat the competition rather than maintaining the application infrastructure.