Architecting and Deploying a Simple Web Application on AWS: A Step-by-Step Guide

Architecting and Deploying a Simple Web Application on AWS: A Step-by-Step Guide

Welcome to the world of web application development on AWS. As an AWS user and professional with an ever-burning desire to make a difference by putting out content to help aspiring cloud engineers in their careers and learning journeys, I am delighted to guide you through the step-by-step process of architecting and deploying a simple end-to-end web app on AWS. In this comprehensive tutorial, we will explore how to combine different AWS services to build and deploy a simple web application that calculates the exponent of a number. The objective of this tutorial is to equip you with the knowledge and skills needed for you to embark on your own web application projects. Whether you are a developer taking your first steps into the cloud or an absolute beginner in the field of cloud computing, this guide will provide you with a solid foundation in AWS web application development. Together, we will build out the application and see how the different AWS services integrate. So, buckle up, embrace the excitement of learning, and get ready to reveal the potential of AWS in your web development endeavours. Let's dive in!

AWS Services used in this tutorial

  • AWS Amplify

  • Amazon API Gateway

  • AWS Lambda

  • AWS IAM

  • Amazon DynamoDB

Prerequisites

  • An AWS account: Make sure you don't use your root user account for this tutorial as it is not good practice to use your root account for everyday tasks. You should rather create a new IAM user with Administrator access and then use it for this tutorial.

  • A basic understanding of AWS.

  • A text or code editor. I am using VSCode. You can use Notepad or any other text editor of your choice.

  • A basic understanding of HTML, CSS and JavaScript is recommended but not necessary.

Architecture Diagram

Below is the visual solution of the architecture we will be building out.

If the diagram doesn't make sense to you yet, no need to be worried. Just view it as a puzzle and by the end of this article, we would have put the entire puzzle together for you to understand what we have built. With that out of the way, let's get started.

Step One: Create an index.html file

The very first step we are going to take on this journey is creating an HTML file. Go on and create an index.html file in any folder of your choice and add the following code to it using a text editor.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600&display=swap"
      rel="stylesheet"
    />
    <title>Exponential Math ⚡️</title>
    <!-- Styling for the web page -->
    <style>
      h1 {
        color: #fff;
        text-align: center;
        text-transform: uppercase;
        font-size: 50px;
        font-weight: 600;
      }
      body {
        background-color: #34495e;
        font-family: 'Rajdhani', sans-serif;
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
      }
      label {
        color: #ffa801;
        font-size: 25px;
        margin-top: 20px;
      }

      label:last-of-type {
        margin-left: 20px;
      }
      button {
        background-color: #05c46b;
        border: none;
        color: #fff;
        font-size: 20px;
        font-weight: bold;
        margin-left: 30px;
        margin-top: 20px;
        padding: 10px 20px;
        font-family: 'Rajdhani', sans-serif;
        cursor: pointer;
      }
      input {
        color: #222629;
        border: none;
        font-size: 20px;
        margin-left: 10px;
        margin-top: 20px;
        padding: 10px 20px;
        max-width: 150px;
        outline-color: #05c46b;
      }
      div {
        width: auto;
        position: absolute;
        top: 35vh;
      }
    </style>
    <script>
      // callAPI function that takes the base and exponent numbers as parameters
      var callAPI = (base, exponent) => {
        // instantiate a headers object
        var myHeaders = new Headers();
        // add content type header to object
        myHeaders.append("Content-Type", "application/json");
        // using built in JSON utility package turn object to string and store in a variable
        var raw = JSON.stringify({ base: base, exponent: exponent });
        // create a JSON object with parameters for API call and store in a variable
        var requestOptions = {
          method: "POST",
          headers: myHeaders,
          body: raw,
          redirect: "follow",
        };
        // make API call with parameters and use promises to get response
        fetch("YOUR_API_GATEWAY_ENDPOINT", requestOptions)
          .then((response) => response.text())
          .then((result) => alert(JSON.parse(result).body))
          .catch((error) => console.log("error", error));
      };
    </script>
  </head>
  <body>
    <div>
      <h1>Exponential Math ⚡️</h1>
      <form>
        <label>Base number:</label>
        <input type="number" id="base" />
        <label>to the power</label>
        <input type="number" id="exponent" />
        <!-- set button onClick method to call function we defined passing input values as parameters -->
        <button
          type="button"
          onclick="callAPI(document.getElementById('base').value,document.getElementById('exponent').value)"
        >
          CALCULATE
        </button>
      </form>
    </div>
  </body>
</html>

Whewww!! that's quite a lot of code. Don't get all worked up if you don't understand what the code does. As I said earlier, it's all good if you don't understand HTML, CSS and JavaScript. This code constitutes our web page i.e. the frontend of our application which we are going to deploy to AWS Amplify.

That's how our final webpage looks. You can play around with the colours to change the look and feel of the webpage. Now, compress your index.html file i.e. make it a zip file. We are going to use this zip file in the next step.

Step Two: Deploying our web app on Amplify

It's finally time for us to get started with the AWS side of things. We are using Amplify for this step. Amplify is a development platform that simplifies building, deploying, and scaling web and mobile apps. It streamlines the development workflow, abstracts backend complexities, and accelerates app development on AWS infrastructure.

Login into the AWS management console and search for Amplify in the search box in the top left-hand corner of the management console. Make sure you are not using your root user account as it is not good practice to do that.

Once you have searched for and navigated to AWS Amplify, scroll down to the bottom of the page and click on the Get Started button that is pointed to by a red arrow as shown in the image below.

Clicking the Get Started button will take you to another page where you choose the source of your code i.e. where Amplify is going to get the code you want to deploy from.

Since the application code we want to host is not on any Git provider such as GitHub, GitLab or any other Git provider, select the "Deploy without Git provider" option and click on the Continue button as shown in the image above.

Clicking on the Continue button takes you to the page where we have to upload our code i.e our index.zip from Step One.

In the App name input box shown in the image, type out the name you'll like to give your app. I named my app ExponentialMath. You can call yours any name of your choice. In the Environment name input box, write dev Now drag and drop your index.zip file into the area that is pointed to by the arrow as shown in the image above. After having done that, click on the save and deploy button.

If the deployment was successful, the resulting page will be similar to the one in the image shown above. Clicking the URL that's on that page will open our webpage. Try opening the URL to make sure that everything is as we expect it to be.

So the front end of our simple web app has been deployed successfully. 🎉 If you try performing calculations on our deployed web app you will notice that it doesn't work. It doesn't work because we have not implemented our application logic (i.e. the calculation functionality) just yet. That's what we are going to do in the next step.

Step Three: Working on our application logic

We are going to use AWS Lambda for this step. Lambda is a serverless computing service that allows developers to run code without provisioning or managing servers. It executes code in response to triggers, such as changes to data in an S3 bucket, updates to a DynamoDB table, or HTTP requests through API Gateway.

Using Lambda, we are going to write some Python code using the Python Math library to carry out the calculations feature of our application. Again, you shouldn't bother if you don't understand the code. Open a new AWS management console window in a new tab and search for lambda using the search bar at the top of the management console window.

Click on the Create a function button as shown in the image above. On the resulting page select Author from scratch, input a name for your function (as you can see in the image below, I called my lambda function ExponentialMathFunction ). Now select the runtime for the function. As pointed out earlier, we are using Python so select the Python runtime.

Leave all the other configuration settings as they are and click on the Create function button. On the resulting page, replace the code in the lambda_function.py file with the code below, use Ctrl + S to save it and then click on the Deploy button to deploy the function.

# import the JSON utility package
import json
# import the Python math library
import math

# define the handler function that the Lambda service will use an entry point
def lambda_handler(event, context):

# extract the two numbers from the Lambda service's event object
    mathResult = math.pow(int(event['base']), int(event['exponent']))

    # return a properly formatted JSON object
    return {
    'statusCode': 200,
    'body': json.dumps('Your result is ' + str(mathResult))
    }

What the code does is, it imports the JSON utility package and the Python Math library which will enable us to do our calculations. Reading the comments in the code will help you understand it a little better.

Now that our lambda function is deployed, let's configure a test event and test it to see if it is working as it should be. As indicated in the image above, click on the dropdown icon on the blue Test button and then click on Configure test event This opens a modal for you to configure and save a test event.

Replace the JSON code shown in the image above with the code below and click on the Save button.

{
  "base": 3,
  "exponent": 2
}

Now that our test event is configured and saved, we can run a test. So click on the blue Test button to run a test for our lambda function.

As you can see in the image above, our test has returned a response with a status code of 200 (which means that the test was successful) and a response body which says our result is 9. This means our function is working just fine. Let's move on to the fourth step.

Step Four: Make it possible to invoke the lambda function we created

After having created a lambda function that will perform our calculations, we need to make it possible for our hosted web application to be able to invoke it when needed. Amazon API Gateway is the AWS service that will come to our rescue to help us achieve this outcome. API Gateway is a fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. APIs act as the “front door” for applications to access data, business logic, or functionality from your backend services (lambda in our case).

As we did for the other AWS services in the previous steps, open a new management console window in a new tab, search for and navigate to API Gateway. On the resulting page, click on the Create API button.

Scroll down to the REST API section and click on the Build button.

Select the options highlighted in the image above, add a name for your API click on the Create API button. On the page that opens after you click the Create API button, make sure you are in the Resources tab.

Make sure you have the forward slash selected as indicated in the image above. Click on the Actions button and select the Create Method option from the dropdown menu that appears.

From the select input that appears below the forward slash, select POST and then click on the check icon that appears next to it. The reason we are selecting POST is that our application submits numbers for a calculation to be performed.

Since we are using a lambda function for our application logic, select Lambda Function as the Integration type, input the name of our lambda function in the appropriate input box and then click on the Save button. On the dialogue box that appears after clicking Save, click OK to permit API Gateway to invoke the lambda function.

The next thing we have to do is enable CORS (Cross Origin Resource Sharing). What CORS does is that it allows resources on one domain to access resources on another domain. Click the Actions button and select Enable CORS. Make sure you have selected POST sitting directly below the forward slash before you select Enable CORS

Click on the Enable CORS and replace existing CORS headers button as indicated in the image above and then click on the Yes, replace existing values button on the dialogue box that appears.

With that out of the way, let's deploy the API so we can be able to test it. Again, click on the Actions button and select Deploy API In the dialogue box that appears, choose [New Stage] in the Deployment Stage select box. For the Stage name, just write dev and then click on Deploy

Copy your Invoke URL and keep it safe somewhere because we are going to use it later on. Now go back to the resource tab and click on POST to see a flow of how the API works.

Click on the blue lightning bolt to open the test page where we will run a test for our API. Pass in the following code in the request body as shown in the image below and then click on the Test button.

{
    "base": 5,
    "exponent": 5
}

The response body of our test indicates that the test was successful and it returns the result of the calculation performed by the lambda function we created earlier. This means API Gateway is functioning properly.

Whewww! That was quite a long step but we have completed it successfully 🎉. Now on to the next step!!

Step Six: Configure a database where we will store all the results of our calculations.

For this step, we are going to use DynamoDB to achieve our objective. Our application doesn't necessarily need to store the results of calculations in a database. This step is added just to show you have you can store application data in a database. DynamoDB is a managed NoSQL database service that provides fast performance with seamless scalability. In DynamoDB, data is stored in key-value pairs.

Open a new management console window in another tab and navigate to DynamoDB.

Click on Create table

Fill in the necessary details as indicated in the image above and click on Create table This creates a new DynamoDB table for us. Click on the table name to open it.

Toggle the Additional info section, copy and keep the ARN of your database table somewhere safe as we are going to use it soon.

Step Seven: Grant our lambda function permission to write results to DynamoDB

For this step, we are going to leverage the IAM roles feature of AWS IAM. IAM is a service provided by AWS that allows you to manage access to your AWS resources. An IAM role is a set of permissions that you can temporarily assign to an entity or an AWS service.

Go back to the browser tab where you have AWS Lambda open.

Make sure you are in the Configuration and Permissions tabs as indicated in the image above. Click on the Role name highlighted in the image. This will open IAM in a new tab.

Click on the Add permissions dropdown button and select Create inline policy as shown in the image above.

On the resulting page that opens, make sure you are in the JSON tab and replace the JSON policy with the code below. Make sure you replace "YOUR-TABLE-ARN" with the actual table ARN you copied earlier. Once you are done, click on the Next button.

{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "LambdaFunctionExecutionPolicy",
        "Effect": "Allow",
        "Action": [
            "dynamodb:PutItem",
            "dynamodb:DeleteItem",
            "dynamodb:GetItem",
            "dynamodb:Scan",
            "dynamodb:Query",
            "dynamodb:UpdateItem"
        ],
        "Resource": "YOUR-TABLE-ARN"
    }
    ]
}

What this policy does is it allows our lambda function to perform various actions on our DynamoDB table.

Input a policy name as indicated in the image above and then click on the Create policy button to create the policy.

Now that we have successfully made it possible for our lambda function to perform various actions on our DynamoDB table, let's update our lambda function. Copy the code below and use it as a replacement for the current code in the lambda_function.py file found in the code tab of our lambda management console window and then use Ctrl + S to save the file.

# import the JSON utility package
import json
# import the Python math library
import math

# import the AWS SDK (for Python the package name is boto3)
import boto3
# import two packages to help us with dates and date formatting
from time import gmtime, strftime

# create a DynamoDB object using the AWS SDK
dynamodb = boto3.resource('dynamodb')
# use the DynamoDB object to select our table
table = dynamodb.Table('ExponentialMathDatabase')
# store the current time in a human readable format in a variable
now = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())

# define the handler function that the Lambda service will use an entry point
def lambda_handler(event, context):

# extract the two numbers from the Lambda service's event object
    mathResult = math.pow(int(event['base']), int(event['exponent']))

# write result and time to the DynamoDB table using the object we instantiated and save response in a variable
    response = table.put_item(
        Item={
            'ID': str(mathResult),
            'LatestGreetingTime':now
            })

# return a properly formatted JSON object
    return {
    'statusCode': 200,
    'body': json.dumps('Your result is ' + str(mathResult))
    }

After updating and saving the code, make sure you deploy it by clicking on the Deploy button. Now click on Test, go back to the DynamoDB tab and open up our database table.

Click on the Explore table items button to see if the results of the test were stored successfully.

As you can see in the image above, the results were stored successfully 🎉.

Step Eight: Make it possible for our front-end to be able to hit the API Gateway Endpoint

We are getting close to the finish line!! As of this point, we can perform calculations by inputting numbers and using the CALCULATE button on our webpage because we can't invoke our calculations lambda function from the webpage. To make this possible, we have to connect our webpage to API Gateway using the API Gateway endpoint we copied earlier.

Replace YOUR_API_GATEWAY_ENDPOINT with the API Gateway endpoint you copied earlier. Save your changes, delete the old index.zip and create a new one by zipping the updated index.html Go back to the AWS Amplify management console window

Drag and drop your index.zip file here so that our application can be redeployed. Now open the link to our webpage hosted on Amplify, input some numbers and click CALCULATE to perform some calculations. Our webpage will call API Gateway which then triggers our lambda function to perform calculations and store the results of the calculations in our DynamoDB table.

Congratulations!! 🎉🎉🎉 You have successfully built an end-to-end, simple web application on AWS. Kudos to you!!⚡️

Before you run off to celebrate, let's do a resource cleanup so that you don't wake up to surprise AWS bills even though we have been using free-tier eligible resources. In this last lap, we are going to shut down all the services we have used starting with DynamoDB. Go back to the DynamoDB management console and perform the following steps.

Now let's go delete our lambda function.

Let's move on to API Gateway.

Lastly, let's clean up Amplify.

Final Words

Congratulations on completing this step-by-step tutorial on building a simple end-to-end web application using Amplify, AWS Lambda, API Gateway, IAM, and DynamoDB! You have embarked on a remarkable journey, unleashing the power of AWS services to create a fully functional application. By following this comprehensive tutorial, you have gained valuable hands-on experience in leveraging the capabilities of AWS Amplify for frontend development, AWS Lambda for serverless functions, API Gateway for managing endpoints, IAM for access control, and DynamoDB for data storage. Armed with this knowledge, you are well-equipped to tackle more complex projects and explore the vast potential of cloud-native application development. Keep honing your skills, stay curious, and continue pushing the boundaries of innovation. Your newly acquired expertise will undoubtedly serve as a solid foundation as you venture into the exciting world of AWS web application development. Don't forget to follow me for more content like this. Happy coding and best of luck in your future endeavours!