Architect and Build an End-to-End AWS Web Application
Published: 2024-07-09 07:56:13

## Introduction:

Architect and build an end-to-end AWS web application with AWS Amplify for seamless front-end hosting, AWS Lambda for serverless functions, Amazon API Gateway for managing APIs, Amazon DynamoDB for scalable NoSQL databases, and IAM roles for secure access management. This integration ensures a robust, scalable, and secure application infrastructure.

## AWS Services used in this tutorial

Amplify
Amazon API Gateway
AWS Lambda
AWS IAM
Amazon DynamoDB

## Prerequisites

1. - An AWS account
2. - A basic understanding of AWS.
3. - A text or code editor. I am using VSCode. 
4. - A basic understanding of HTML, CSS and JavaScript is recommended but not necessary.

## Architecture Diagram

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l564g5o6olub6m3j1o6g.jpeg)

## Step 1: Create an index.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>

```
 This code constitutes our web page i.e. the frontend of our application which we are going to deploy to AWS Amplify.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ambit014bgogb04f7baj.png)

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 2: Deploying our web app on Amplify
 
We are using Amplify for this step.AWS Amplify is a set of tools and services for building scalable mobile and web applications, providing backend services, front-end libraries, and hosting solutions with a focus on ease of use.
Login into the AWS management console and search for Amplify in the  management console. Once you have searched for and navigated to AWS Amplify.

1.Click on 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.)
2.Select the “Deploy without Git provider”.(Since the application code we want to host is not on any Git provider and Clicking on the Continue button takes you to the page)
3.Type out the name you’ll like to give your app. I named my app ExponentialMath.
4.In the Environment name input box, write dev Now drag and drop your index.zip file into the area.
5.Next click on the save and deploy button.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlbm0aqjnbvvwdwf93zd.png)

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.

## Step 3: Working on our application logic

We are going to use AWS Lambda for this step.AWS Lambda is a serverless computing service that runs code in response to events, automatically managing the underlying infrastructure, allowing you to focus solely on code without provisioning servers.

Using Lambda, we are going to write some Python code using the Python Math library to carry out the calculations feature of our application.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnydmwo40c8wh8a44yyw.png)

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3av5wysx95ym8ont6jn.png)

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/knlxheds48zv6y63uzgp.png)

Now that our lambda function is deployed, let’s configure a test event and test it to see if it is working

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ey9yk76e2mdfjj4j04kc.png)

Replaced 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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wetr4fo79jfp82euhhxb.png)

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 4: 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 a managed service that allows developers to create, publish, maintain, monitor, and secure APIs at any scale, enabling backend services for web, mobile, and serverless applications.

1. Open a new management console window in a new tab, search for and navigate to API Gateway.
2. Click on the Create API button. 
3. Scroll down to the REST API section and click on the Build button.
4. Add a name for your API click on the Create API button.(make sure you are in the Resources tab.)
5. Make sure you have the forward slash selected as indicated.
6. Select the Create Method option.
7. 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.
8. 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.

9.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

10.let’s deploy the API so we can be able to test it.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb02pgcagi5vvq1x1wtj.png)

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 Test .

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swxfdtioc9wkf24epsyb.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rsjootwjlx8bvfpc5go.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/amza9jt7keuvmqkruq3m.png)

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

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0lgvq34htxc8euu2dps.png)

Click on Create table

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x05nn6moytvb0tfjxs3j.png)

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kz4rgzzx1ubmp1ljwv0d.png)

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

## Step 6: Grant our lambda function permission to write results to DynamoDB

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hvo9bvcfk8s7tvi5rmiw.png)

Click on the Role name highlighted in the image. This will open IAM in a new tab.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w39dt74f9cpn0fbs20ca.png)

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

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h31gbs13bg5uqcx058rp.png)

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))
    }
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2igaibavkr61xushe5m.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auteafep2epzbpvxq69g.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n26x4yuxgl8recebeji6.png)

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.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/39ifmfw5ngh4t08uosho.png)

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l524s3umw199nw9iwj1x.png)

the results were stored successfully.

## Step 7: 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.

```
<!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>
```
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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q4cwwzpi42kp9dlnnsdp.png)

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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvvp6w57oq26uhi2bucx.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/77frax5ickn5nab87rh1.png)

Congratulations!!!You have successfully built an end-to-end, simple web application on AWS. 

## Conclusion

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