How to Create Web Apps with AWS Serverless Microservice Patterns [Part 2]

June 09, 2021



In the first part of this blog post, we talked about serverless microservice patterns and started building a human resources application using The Simple Web Service pattern. We covered some aspects of building a serverless API with Amazon APIGateway, using serverless functions in AWS Lambda with NodeJS runtime environment, and handling authentication in web applications using AWS Cognito. We’ve already built a web application where Admin users can manage their company departments and employee accounts.

Now, we will focus on the data management of users of our application. In this part, we will build a data source which is going to be AWS DynamoDB for storing employee information, and a custom GraphQL API that we named EmployeeActions for interacting with that data source from our front-end application. We will use AWS Appsync for building our GraphQL API. When we will be done, our users will be able to perform CRUD operations on their records and see their colleague’s personal information such as birthdays.

Image shows The Simple Graphql Service Pattern

The Simple GraphQL Service Pattern

Before we start, please beware that this article assumes you have a basic understanding of instrumenting ReactJS and AWS serverless application development framework, called AWS Amplify.

The Simple GraphQL Service Pattern

  1. Setting up the Development Environment
  2. Create DynamoDB and Connect with GraphQL API
  3. Connect Front-End to API

1. Setting up the Development Environment

First, we must install Amplify CLI on our local machine. Before starting, make sure that the following technologies are installed:

  • Node.js v12.x or later
  • npm v5.x or later
  • git v2.14.1 or later

You can install Amplify CLI with an npm package manager.

npm install -g @aws-amplify/cli

Then you must configure your AWS account with Amplify CLI by using:

npm install -g @aws-amplify/cli

<a href=https://www.youtube.com/watch?v=fWbM5DLh25U target=_blank >The official AWS Amplify setup guide will help you to install and configure the AWS Amplify CLI. When completing the installation process, navigate the root folder of our application and run this command:

amplify init

That will ask some questions about your application such as the project name, environment name, build commands, etc. Choose the default values and hit enter.

The next step is to install the necessary dependencies:

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

The last part of setting up Amplify is adding this code to your index.js file. This will configure Amplify and allow you to use your cloud resources with your front-end application.

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

2. Create DynamoDB and Connect with Graphql API

You can create back-end resources with just amplify add command. We can start building our GraphQL API with this command:

amplify add api

It will ask some questions to provide cloud resources for you. Choose the ones that meet your demands. Defaults will be fine in our demo application.

Now comes the fun part: it is time to design our GraphQL schema. When you hit yes for the “Do you want to edit the schema now?” question, your text editor will open the schema.grapql file for you. In case the file doesn’t get open, you can always find your schema file in amplify/backend/api/{{apiName}}/schema.graphql directory of your application.

For our PurpleHR application, we must store the personal information of our employees, such as name, phone number, birthday, etc. So, we must define an Employee Type in our graphql schema.

type Employees
  @model
  @auth(
    rules: [
      { allow: owner, operations: [read, update] }
      { allow: groups, groups: [Admins], operations: [create, read, update, delete] }
      { allow: groups, groups: [DevOpsTeam, GrcTeam, MarketingTeam, SecurityTeam], operations: [read] }
    ]
  ) {
  id: ID!
  username: String
  firstName: String
  lastName: String
  phoneNumber: String
  department: String
  certifications: String
  birthDay: String
  university: String
  uniDepartment: String
  graduationYear: String
  workStartDate: String
  hobbies: String
  favoriteTeam: String
  hometown: String
}

@model directive will provide an AWS DynamoDB table with the given name, connect it with AWS Appsync with all possible CRUD operations ready for our use and create necessary permissions with AWS IAM.

With @auth directive, we are defining our authorization rules about our type. Remember we have different user groups for different kinds of users on our application. In this example, Admin users can have permissions for all CRUD operations on Employee type. However, individual employees that are a member of one department group can only view the Employee type. After finishing the design of our schema, we can create resources in the cloud by running this push command.

amplify push

It will take up to 5 minutes and then we will have a running live API with all necessary permissions and connection to AWS DynamoDB.

3. Connect Front-End to API

Now it is time to provide users a nice UI for interacting with our application. To make it happen, we need to use an API Client for handling requests from the client-side. We will use graphql client that comes with Amplify package. Let’s build a sample page to list all employees that are in DynamoDB. To achieve this, we must make a list request to our Graphql API, and with the help of AWS Appsync resolvers, we will interact with AWS DynamoDB and get the result then bring it to the front-end.

Open src/App.js file and import React and necessary Amplify packages.

import React, { useState, useEffect } from react;
import { API, graphqlOperation } from aws-amplify;

Then imports the listEmployees query. You can find it in src/graphql/queries.js

import { listEmployees } from ./graphql/queries;

Now we need React state for storing our employee information and show it from the front-end. Since we have already imported the useState from react, in first-line now we can use it like this.

const [employees, setEmployees] = useState(null)

Initially, it starts will a null value because, in the beginning, we don’t have an employee until we get results from the back-end.

Create a JavaScript function that runs our list query and fetches results then sets it to our state.

  async function fetchEmployees() {
    try {
     const result = await API.graphql(graphqlOperation(listEmployees))
     setEmployees (result.listEmployees.data.listTodos.items)
    } catch (err) { console.log('error fetching employees) }
  }

Now, we must run our function when the app mounts for the first time. To make it happen, we must use a side effect called useEffect. It is a React Hook that manages the lifecycle of React components. We imported it in the first line.

useEffect(() => {
  fetchEmployees()
}, [])

As you can see, the dependency array of the useEffect is empty because we only want to run this side effect just once when the app mounts. Now, we have our employee state which means that we are ready to render some JSX. In the return part of our component, we can map all employees and return their names for now.

return (
  <div>
    <h2>PurpleHR</h2>
    <h3>Employee List</h3>
    {employees && (
      <ul>
        {employees.map((employee) => (
          <li key={code.id}>
            {employee.firstName} - {employee.lastName}
          </li>
        ))}
      </ul>
    )}
  </div>
)

Finally, our App.js will look like this:

import React, { useState, useEffect } from 'react'
import { API, graphqlOperation } from 'aws-amplify'
import { listEmployees } from './graphql/queries'

const App = () => {

const [employees, setEmployees] = useState(null)

  useEffect(() => {
    fetchEmployees ()
  }, [])

  const fetchEmployees = async ()  => {
    try {
     const result = await API.graphql(graphqlOperation(listEmployees))
     setEmployees (result.listEmployees.data.listTodos.items)
    } catch (err) { console.log('error fetching employees) }
  }

 return (
    <div >
        <h2>PurpleHR</h2>
        <h3>Employee List</h3>
          {employees && (
            <ul>
              {employees.map((employee) => (
                <li key={code.id}>
                  {employee.firstName} - {employee.lastName}
                </li>
              ))}
            </ul>
          )}
        </div>
  )
}
export default App

In this blog post series, we have covered how to successfully create serverless web applications from scratch using AWS services. We have learned how to add authentication and authorization to web applications, create a GraphQL API and use it in front-end React applications. We will be sharing more posts related to these topics, stay tuned!