Build an End-to-End Web Application with AWS: Step-by-Step Guide
Tags: aws, beginners, webdev, tutorial
Published: 2023-07-12 08:34:18

In this blog post, we will walk through the steps involved in building an end-to-end web application using AWS services.

**Overview of the services and application we'll be building:**

The application we will build is a simple calculator. Users will be able to enter two numbers and the application will calculate the result. The result will be stored in a DynamoDB table and the user will be able to view it again later.

**Architect the web application from scratch:**

The first step is to architect the web application. We need to decide how the different services will interact with each other.

The user will interact with the application through a web page. The web page will be hosted on Amplify. When the user clicks the "Calculate" button, the web page will send a request to the Lambda function. The Lambda function will perform the calculation and store the result in the DynamoDB table. The web page will then retrieve the result from the DynamoDB table and display it to the user.


![Web Application Architecture diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1ra1k1xfld4yeqj2p5v.png)

We will use five AWS services:

![Five AWS Services that we will in this guide](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eio5ri9lm97a7r67s07o.png)
-   **AWS Amplify:** Amplify is a service that makes it easy to build and deploy web applications.
-   **AWS Lambda:** To perform math calculations. Lambda is a service that lets you run code without provisioning or managing servers. 
-   **Amazon DynamoDB:** To store the results of the math calculations. DynamoDB is a service that provides a NoSQL database.  
-   **Amazon API Gateway:** To expose the math functionality to users. API Gateway is a service that helps you create, publish, and manage APIs.
- **IAM:** To manage permissions.

**Prerequisites:**
Before you follow along with this tutorial, you will need the following:
-   An AWS account. 
-   A basic understanding of AWS services.
-   A text editor.

### Step 1: Create a Web Page with Amplify

We will use Amplify to create and host a simple web page. The web page will simply display the name of our application.
Before getting started with Amplify create an `index.html` file with the following code:

```
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>To the Power of Math!</title>
</head>

<body>
    To the Power of Math!
</body>
</html>
```
 
Or you can copy the `index.html` [this Gist](https://gist.github.com/akiltipu/16f64b2af8e7f11fc28ac43a2655b86a)

and zip this file name **index.zip**. Now,

1.  Go to the Amplify Console: [https://console.aws.amazon.com/amplify/home](https://console.aws.amazon.com/amplify/home).
2.  Select Amplify Hosting, Click Continue
3.  Select Deploy without Git provider for manual deployment.  
4.  Enter a name for your project and an environment name.

![Naming the app on Amplify](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2w96ti4kiglvkx6tzkdl.png)

5.  Drag and drop the index.zip file from your local machine into the Method field. 
6. Click Save and Deploy.

Once the deployment is complete, you will see a Deployment a successfully completed message and link to your web page. Open the link in your browser to view the web page. 


![Deployed On Amplify](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4frxf74kxvrr6j4zko4.png)

And your live website should look like this:

![Apmlify live website](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8kmxzry7qcmppjinbnyo.png)

### Step 2: Create a Lambda Function to Perform Math Calculations

We will use a Lambda function to perform the math calculations that our users will need. The Lambda function will take two numbers as input and return the calculation result.

 1. Go to the Lambda Console: https://console.aws.amazon.com/lambda/home.


![Creating AWS Lambda Function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/769rw9emly0cq24q4519.png)

2.  Click ***Create a function.***   
3.  Select the ***Author from scratch.***
4.  Enter a name for your Lambda function.    
5.  Select the latest Python 3.10 runtime (Architecture: x86_64).  
6.  You can leave everything else the same.
7. Click ***Create function.***

![AWS Lambda Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2l39tdwvj0j0bddhhb4.png)

The Lambda function code will be pre-populated with some sample code. Replace the sample code with the following code:

```
# 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))
    }
```
Or [Copy the code from this Gist](https://gist.github.com/akiltipu/a3ee36a771dea5e9589e41772c4fba25)

We have a simple code that imports the `JSON` utility package and the Python math library. The lambda handler is where most of the work is done. The user passes in two numbers, the base number, and the exponent, which are extracted from the event object. We use `math.pow` function to calculate the result. Finally, we return the result in a `JSON` object.

Now make sure to save the code by `Ctrl + S` and very importantly **Deploy** the code.

**Testing our Lambda function:**
To test our Lambda function, set up a new test event by clicking the dropdown arrow next to the "test" option. 


![Creating AWS Lambda Function Test event](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrrrb9y7opg7tvkne8k0.png)

Create an event named "PowerOfMathTestEvent" and leave it private. We only need two values: the base and the exponent. You can choose any values you want. For example, I'll use 2 as the base and 3 as the exponent, which should give us 8. Scroll down and save.

We've finished configuring the test event, and now it's time to run the test. Simply click the Test button. If everything is working correctly, you should receive a status code of 200 and the result should be 8. This confirms that our Lambda function is functioning properly. 


![AWS Labmda Test result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xsiaeg808vdz2y746p3w.png)

So far, we have a basic HTML page hosted on Amplify and a Lambda function for performing simple math operations. Now, our next step is to find a way to invoke the Lambda function without users having to access the AWS console. We need a public endpoint or URL that can trigger the function to run. To achieve this, we'll utilize API Gateway, a core AWS service for building APIs (HTTP, REST, or WebSocket). API Gateway is the ideal method to invoke our Lambda function. Let's proceed with setting it up.

### STEP 3: Creating a REST API for our Lambda function using API Gateway

We're now going to create a REST API for our Lambda function using API Gateway. Go to the Lambda Console: https://console.aws.amazon.com/apigateway/home
Click Create API button and from REST API select Build. Select "REST API" and click on the "New API" button to begin. 


![Creating New API from API Gateway](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zv2wop1mi2u5izhcjkn9.png)

Leave the default options and give it a name, such as "**PowerOfMathAPI**" and endpoint type Regional click on "**Create API**" to proceed.

Currently, our API is empty, so let's add a method. In the left panel, select "**Resources**," then select the backslash ("**/**"). From the actions menu, choose "**Create Method**." Select "**POST**" as the method type since we'll be receiving data from the user. **Save** the method.


![Creating API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75bw8asindhw6a9v1li0.png)

For the integration type, choose "**Lambda Function.**" Enter the name of the Lambda function, such as "**PowerOfMathFunction**." Save your changes.


![Creating New API using POST method](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vu4v1act9k2b16nt26av.png)

Next, we need to enable Cross-Origin Resource Sharing (CORS). To do this, ensure that "**POST**" is selected in the API Gateway console. Then, from the actions menu, choose "**Enable CORS**." This allows a web application on one domain to access resources on a different domain. Since our web application is hosted on Amplify and our Lambda function will run on another domain, we need to enable CORS for them to work together. Simply click on "**Enable CORS**" and **confirm** your selection.


![Enabling CORS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s33rtzt9h2fmjrf46apz.png)

Now, let's deploy the API to test it out. In the API Gateway console, click on the actions menu and choose "**Deploy API**." We'll set up a new stage, which we'll name "**dev**" for this example. Click on "**Deploy.**"


![Deploying API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0357dfynb4ko7uifyg41.png)

To validate our setup, go to the left panel, click on "**Resources,**" and select "**POST.**" Here, you'll see the flow of the request from the method to the integration with Lambda and back to the client. To test it, click on the blue lightning **bolt icon.**


![Testing API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lme9r7yv5h0qob85c1en.png)

You can send test data in a simple format, such as `{"base”: 2, “exponent”: 4}` Click on "**Test**," and you should receive a result of 16. Great!


![Showing Test result of API](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adn2qx49m5vz9x4ezkvv.png)

This progress means we can now trigger our math function in Lambda with an API call.

Now, we can trigger our math function in Lambda with an API call. However, we still need to connect it to our `index.html` page and Amplify, but we'll address that soon.

Next, let's explore how to incorporate a database into our setup. We want to store the math results and also return them to the user. While it's not necessary to store the results, most real-world applications use databases. We'll use DynamoDB, a lightweight NoSQL database that doesn't require upfront schema and relationship setup like relational databases.

Additionally, we need to handle permissions between different parts of the application. In this case, we need to grant our Lambda function permission to write to the database. Let's proceed with configuring this aspect.

### Step 4: Create a DynamoDB Table to Store the Results

To store our math results, let's create a new DynamoDB table. Go to the DynamoDB Console: https://console.aws.amazon.com/dynamodb/home

![Creating Table on AWS DynamoDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7fc29vir3c41qmjweie.png)


Click on "**Create table**" and provide a table name, such as "**PowerOfMathDatabase.**" For the partition key, let's use "**id**." Leave the remaining settings as default and **create** the table.

Now, it's important to save the **Amazon Resource Name (ARN)** for this table. Go to the table details, specifically the "**General information**" and "**Additional info**" sections. Copy the ARN and keep it anywhere that will be needed later.

Let's grant our Lambda function permission to write to the DynamoDB table. Go back to the Lambda function "**PowerOfMathFunction**" (or locate it in the Lambda console). Access the configuration tab and navigate to the "**Permissions**" section. Click on the **role name**, which will open in a new tab.


![AWS Lambda Permission setep](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/piqmcxy9ibdvfbmlol7t.png)

To add additional permissions for DynamoDB, click on "**Add permissions**" on the right side. Then, choose "**Create inline policy**" and select the JSON tab.


![Adding permission and Creating Inline Policy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5mqvxwomy8b474ibbc5c.png)

Copy JSON code below or from this [Gist](https://gist.github.com/akiltipu/9651d5297e7b6f81fb036429a5fb3e66) and replace the existing code.

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

The JSON code grants various DynamoDB actions to the Lambda function. However, make sure to update the table **ARN** in the code. Retrieve the ARN you previously saved and replace it accordingly. 

![Specify Permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8o9fwd927ois0px9nba.png)

Review the policy, give it a name (e.g., "PowerOfMathDynamoPolicy"), and click on "**Create policy**."


![Review and Create Policy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n90mc6cq3hbvdqdjs4vc.png)
With the permissions in place, we have completed this step.

**Updating the Lambda function code to write to the DynamoDB table**

Now, let's update the Lambda function to interact with the database. Go back to the Lambda function "PowerOfMathFunction" and access the code. Replace the existing code with the updated code provided below or copy from this [Gist](https://gist.github.com/akiltipu/399788037680b3a93e5adcac05210046). 

```
# 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('PowerOfMathDatabase')
# 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))
    }
```
Ensure that you modify the table name if necessary.

The updated code includes new imports for the AWS SDK (boto3) and packages for date and time formatting. It retrieves the DynamoDB table resource, gets the current time, and inserts the math result along with the timestamp into the table using the table.put_item function.


![Updated AWS Lambda Function Source Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fm5etkrdxvleka9yls2.png)

After pasting the updated code, save the changes and deploy the function to apply the updates. Then, test the function using the same test case. Verify that the result is still correct. Additionally, check the DynamoDB table and explore the table items button to see the stored result.


![Showing DynamoDB table data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17ap1m15jwtgzx5pezg6.png)

With the Lambda function now writing data to the DynamoDB table and having the proper permissions, we're making progress.

We're almost there! However, you might have noticed that we're missing a connection between Amplify and API Gateway. To address this, we need to update the index.html page.

Open the `index.html` file and replace its contents with the provided code from this [PowerOfMath_index_updated.html Gist](https://gist.github.com/akiltipu/2d5f77e5421eb27ffdd8c777c83fe42a) 

The updated code includes some styling enhancements and a form where users can input their numbers. The magic happens with the "`callApi`" function, which connects to the API Gateway endpoint. ***Make sure to update the endpoint URL with your own APIGateway Invoke URL.***

Save the changes to the `index.html` file. Create a `zip` file containing the updated file, and delete the previous zip file if necessary. Drag and drop the new zip file into Amplify, and it will automatically redeploy.

Once the deployment is successful, refresh the page. You will now see the updated web application with the new styling and functionality.


![Updated web application with new styling and functionality](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/42wr4ivwkb6ykw8qesfv.png)

Let's go through what happens when we use the web application. We enter our numbers, for example, 3 to the power of 5. When we click the calculate button, the script on the HTML page calls API Gateway. This triggers the Lambda function, which performs the calculation and writes the result to the database. Finally, we receive a message in the browser through API Gateway. Let's try it out. Calculate... and the result is 243.


![Final Web App with Functionality](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4lwdoygondrtl01kq14i.png)

That's it! Our application is complete. Well done! Feel free to share the link with your friends if you'd like.

### IMPORTANT!! Delete your resources (I'll show you how)
  
Before we conclude, it's essential to delete all the resources we created to avoid **unexpected charges**. Follow these simple steps to delete everything:

1.  Close any tabs related to the application.
    
2.  In the Amplify console, click on "**Actions**" in the top right corner and select "**Delete app**." Confirm the deletion.
    
3.  Move to DynamoDB and click on "**Tables.**" Locate the "**PowerOfMathDatabase**" table and choose "**Delete.**" Confirm the deletion.
    
4.  Delete the Lambda function by selecting "**PowerOfMathFunction**" and clicking on "**Actions**" and "**Delete**."
    
5.  Lastly, go to API Gateway, click on "**APIs**," and select the appropriate API. Choose "**Actions**" and "**Delete**" to delete it.

And that's it! By following these steps, you have successfully removed all the resources we created. If you found this helpful, consider liking and follow me for more content. ***Thank you!***