Create AWS Serverless App

  1. GitHub Repository Link

  2. AWS Site

» What are we building?

In this, blog we will build a serverless web app using several AWS services.

We’ll be using a variety of AWS services to create a seamless, scalable, and highly available system.

To look at the demo, please visit the youtube video and look at the demo sectiion.

» Overview

Let me just give you a quick overview of what will happen when users access our contact form.

AWS

So user will interacts with our application through a web browser or a mobile app.

Amazon S3 - Then the user’s requests will first get directed to Amazon S3, where our static website or client-side code is hosted. S3 serves as a highly durable and scalable storage solution.

Amazon API Gateway - Next, the request is routed to the API Gateway. API Gateway manages the requests and triggers Lambda functions.

AWS Lambda- The API Gateway will trigger our AWS Lambda functions. Lambda executes our backend logic, storing and retrieving data from DynamoDB.

Amazon DynamoDB - For data storage, like storing user info, our Lambda function interacts with Amazon DynamoDB, it’s a fast and flexible NoSQL database service. It ensures low latency and scalability, making it ideal for our application’s needs.

AWS IAM (Identity and Access Management) - Then we have IAM plays a crucial role in securing our application. It manages access to AWS resources, ensuring that each service can only access what it needs to perform its function. This minimises security risks.

Amazon CloudWatch - Throughout this process, Amazon CloudWatch is monitoring our application. It provides actionable insights, collects and tracks metrics, and logs all activities, allowing us to maintain and optimise the system effectively.

» Folder Structure

Refer to GitHub Repository if you get confused somewhere. Link is provided above.

- /aws-serverless-app
  - s3-static-site/
    - index.html
    - style.css
    - script.js
  - lambda-function/
    - index.js
    - package.json

» Create Static website

Let’s start by creating a static website. Create a new project directory and name it (aws-serverless-app) or call it whatever you want and inside it create a folder named: s3-static-site and navigate to this folder and then create five files: index.html, script.js, style.css, success.html, fail.html.

mkdir aws-serverless-app
cd aws-serverless-app
mkdir s3-static-site
cd s3-static-site
touch index.html
touch script.js
touch style.css
touch success.html
touch fail.html 

Create another folder inside the main project and name it lambda-function and inside it create a file named index.js

mkdir lambda-function
cd lambda-function
touch index.js  

Open this project in VS code and let’ s start by brining in the code for our static site.

<!-- index.html  -->
<!DOCTYPE html>
<html>
<head>
  <title>Contact Form for AWS</title>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div class="container">
    <h1>Contact Form</h1>
    <form id="contact-form">
      <label for="name">Name:</label>
      <input type="text" id="name" name="name" required>

      <label for="email">Email:</label>
      <input type="email" id="email" name="email" required>

      <label for="subject">Subject:</label>
      <input type="text" id="subject" name="subject" required>

      <label for="message">Message:</label>
      <textarea id="message" name="message" rows="5" required></textarea>

      <button type="submit">Submit</button>
    </form>
  </div>
  
  <script src="script.js"></script>
</body>
</html>

We’ll start by creating our contact form in the index.html file.

Copy Paste the css code in style.css file.

body {
  margin: 0;
  padding: 0;
  font-family: Arial, sans-serif;
  background-size: cover;
  background-position: center;
  background-color: rgb(46, 46, 46);
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.container {
  width: 500px;
  margin: 0 auto;
  padding: 20px;
  background-color: rgba(255, 255, 255, 0.932);
  box-shadow: 0 0 20px rgba(255, 255, 255, 0.5);
  border-radius: 10px;
}

h1,
p {
  text-align: center;
}

.error {
  color: red;
}

form label {
  display: block;
  margin-bottom: 8px;
}

form input,
form textarea {
  width: 100%;
  padding: 10px;
  margin-bottom: 15px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

form button {
  width: 100%;
  padding: 10px 20px;
  background-color: #007bff;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

form button:hover {
  background-color: #0069d9;
}

Copy script.js: JavaScript for Form Handling.

document
    .getElementById("contact-form")
    .addEventListener("submit", function (event) {
        event.preventDefault();

        var name = document.getElementById("name").value.trim();
        var email = document.getElementById("email").value.trim();
        var subject = document.getElementById("subject").value.trim();
        var message = document.getElementById("message").value.trim();

        var emailRegex = /^\S+@\S+\.\S+$/;
        if (!name || !email || !subject || !message || !emailRegex.test(email)) {
            alert("Please fill in all fields with valid inputs.");
            return;
        }

        var formData = {
            name: name,
            email: email,
            subject: subject,
            message: message,
        };

        submitForm(formData);
    });

function submitForm(formData) {
    fetch("https://kn2h2qbh8i.execute-api.ap-southeast-2.amazonaws.com/dev/submit", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify(formData),
    })
        .then(function (response) {
            if (response.ok) {
                window.location.href = "success.html";
            } else {
                window.location.href = "fail.html";
            }
        })
        .catch(function (error) {
            console.error(error);
            window.location.href = "fail.html";
        });
}

Here’s how our backend processes the form submission:

The API Gateway receives the form data and triggers a Lambda function which we will create now.

The Lambda function will contain the logic to process the form data. And it will include storing the data in DynamoDB.

Create success.html file:

<!DOCTYPE html>
<html>
<head>
  <title>Thank You</title>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div class="container">
    <h1>Thank You!</h1>
    <p>Your message has been submitted successfully.</p>
  </div>
</body>
</html>

Create fail.html file:

<!DOCTYPE html>
<html>
<head>
  <title>Error</title>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
  <div class="container">
    <h1 class="error">Error!</h1>
    <p>Your message was not sent. Please Try Again.</p>
  </div>
</body>
</html>

» AWS Lambda Function

Now we will focus on an AWS Lambda function that processes form submissions and stores the data in DynamoDB.

Open index.js file present in lambda-function directory:

const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
const { v4: uuidv4 } = require('uuid');

exports.handler = async (event) => {
  try {
    console.log('Raw input data:', event); 

    const formData = {
      name: event.name,
      email: event.email,
      subject: event.subject,
      message: event.message,
    };

    const item = {
      SubmissionId: generateUUID(), 
      ...formData,
    };

    await storeFormData(item);

    return {
        statusCode: 200,
        body: JSON.stringify({ message: 'Form submitted successfully' }),
      };
    } catch (error) {
      console.error(error);
      return {
        statusCode: 500,
        body: JSON.stringify({ message: 'Error submitting the form' }),
      };
    }
  };
  
  async function storeFormData(item) {
    const params = {
      TableName: 'ContactFormEnteries',
      Item: item,
    };
  
    await dynamodb.put(params).promise();
  }
  
  function generateUUID() {
    return uuidv4();
  }

Next open the terminal and navigate to the lambda directory and run the command: npm init -y and then install the aws-sdk package as a dependency.

npm init -y
npm i aws-sdk

This will allo Lambda function for interacting with DynamoDB and other services.

Okay, now once we are done with the code let’s work with AWS services.

» Create S3 bucket

Now let’s create a S3 bucket for static site: This bucket will be publicly accessible so that users can access our web application. Let’s get started!

Open your web browser and Navigate to the AWS Management Console and log in with your credentials.

In the AWS Management Console search bar, type “S3” and select “S3” from the dropdown list.

AWS

Click on “Create bucket”.

AWS

And Enter a unique name for your bucket so choose something specific to your project, I’ll go with s3-static-site-2.

And leave rest to default.

Create the Bucket: Next, Click the “Create bucket” button at the bottom of the page to finalize the creation of your S3 bucket.

Next we have to Upload our Website Files.

Click on our Bucket that we just created to open it.

Then Click on the “Upload” button.

AWS

Click “Add files” and select the HTML, CSS, and JavaScript files from our project. And then click on “Upload” to start the upload process.

Next we have to Set Bucket Policy for Public Access: By default, S3 buckets are private. Go to the “Permissions” tab of your bucket.

AWS

Scroll down to the “Bucket policy” section and click on “Edit” and uncheck “Block all public access”. Confirm the changes by checking the acknowledgment box.

AWS

And then we have to Add Bucket Policy:

AWS

Copy and paste the following policy, and replace YOUR_BUCKET_NAME with the name of your bucket:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<your-bucket>/*"
        }
    ]
}

Then let’s Configure Bucket for Static Website Hosting:

Bucket Properties:

Go to the “Properties” tab of your bucket.

AWS

Scroll down to the “Static website hosting” section and click on it.

AWS

We have to Enable Static Website Hosting: So scroll down till Static website hosting section and click on edit

Select Enable and

Specify the index document that is our index.html.

Specify an error document as fail.html. This is optional you can just leave it as it is

Click “Save” to apply the settings.

Then we will have the url created for our static site.

AWS

Click and open the url and there we will have our static website up and running. But it wouldn’t take our data.

» Create Dynamo Table

Let’s start by Creating the DynamoDB Table

In the AWS Management Console search bar, type “DynamoDB” and select it from the dropdown list.

AWS

Then Create a New DynamoDB Table

Go to the tables section and Click on “Create table.

AWS

And Enter a unique name for your table. For example, ContactFormEnteries.

Set the Partition key to SubmissionId with the type String. This will uniquely identify each form submission.

Leave rest to default.

Last, Click the “Create table” button to finalize the creation of your DynamoDB table.

» Create Lambda Function

Let’s now create lambda function. In console search for lambda and create a function, name it whatever you’d like to call it. I Will give it as lambda-function

Then we have to upload our lambda code.

So before that let’s open the index.js file present in lambda-function folder and replace its dynamodb table name with the name that we gave to our DB.

AWS

Open your terminal: Navigate to the directory where your Lambda function code is located. And run:

zip -r lambda.zip .

This command will create a deployment package called lambda.zip that contains all the files and directories needed for our Lambda function.

» S3 bucket for Lambda

Now we will have to create another S3 bucket to upload our lambda code.

AWS

So in serach type S3 and Create a new bucket and name it whatever you want and then let’s upload the zip file of our lambda code here.

AWS

Copy the S3 URI.

» Upload Lambda code

Next open lambda and click on upload from and select Amazon S3.

AWS

And then it will ask you to Paste the S3 uri link and click save.

AWS

» Test Lambda Code

Now let’s test our backend code. So for that we will create a test event. Open the lambda function you created.

Navigate to the test tab and click on create a new event and name anything for your event name.

AWS

And have a name, email, subject and a message json object.

AWS

And click on test button and you should get a successful message.

But it also says: an access error to DynamoDB since you haven’t created any role for the Lambda function to associate it with the database yet.

» Modify Permission for Lambda role

So let’s modify the permissions for lambda role.

Head back to the lambda service and open your function and then Go to the configuration tab and then click on this permission tab and click on role name.

AWS

It will open up another window.

And in there you will have this Add Permissions button click on it. Then from dropdown select Attach Policies

AWS

And Search for AmazonDynamoDBFullAccess and select it. This policy allows full access to DynamoDB.

AWS

After that open lambda again and create another test to check if the data is saved in dynamodb. And I assure you that this time the data will be saved.

Paste this code and click on the test.

{
  "name": "name",
  "email": "[email protected]",
  "subject": "test-for-lamda",
  "message": "Should work"
}

» Set up API Gateway and deploy the endpoint URL

Now, we’re going to set up API Gateway to expose our Lambda function via a RESTful endpoint. This will allow our front-end application to submit form data to our Lambda function. We’ll go through the process of creating an API, configuring it, and deploying it to get a public endpoint URL.

We will create an API in API Gateway. So search for API gateway. Click on create API.

AWS

Select “Build” under the REST API option.

AWS

Then click on new api and Enter a name for your API, such as ContactFormAPI.

AWS

Finally Click on “Create API” to finalize.

» Create Resource

Next go back to your API and click on Create a Resource

AWS

For Resource Name Enter submit.

Click “Create Resource.”

Now next thing we have to do is Create a POST Method:  Select the /submit resource.

AWS

Click on Create Method.

Choose POST from the (/) dropdown list.

AWS

Select your “Lambda Function.”

Click Create Method Button.

» Enable CORS for API

Select the Enable CORS option. Check every boxes and provide our s3 static website URL.

AWS

AWS

» Deploy the API

Click on “Deploy API.”

AWS

Choose “[New Stage]” to create a new deployment stage.

AWS

Enter a name for your stage, such as dev.

Click “Deploy.”

Now let’s Get the Endpoint URL that we will use it to make the request to it from the static website.

fter deploying, you will see a “Stage Editor” view.

AWS

Note the “Invoke URL” at the top. This is the URL endpoint for our API.

Grab this invoked url and let’s make a change in our script.js file present in our static site folder.

Remove the invoke url and replace with the url that we got and go back to s3 bucket and upload the file again.

Now we will enable cors on our S3 bucket. Enabling CORS allows our web application hosted in one domain to interact with resources in our S3 bucket that might be hosted in another domain, which is essential for accessing our static assets like JavaScript, CSS, and HTML files.

» CORS for Satic Site.

Find the bucket you created earlier for hosting your static website and click on its name to open the bucket details.

Click on the “Permissions” tab within your S3 bucket.

Scroll down to the “Cross-origin resource sharing (CORS)” section.

Click on “Edit” to modify the CORS settings.

AWS

Copy and paste the following CORS configuration into the editor:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "POST"
        ],
        "AllowedOrigins": [
            "http://your-static-site-url.amazonaws.com"
        ],
        "ExposeHeaders": []
    }
]

Click “Save changes” to apply the new CORS configuration.

» Test

Now test the app again.

Fill in the form and there you will be redirected to the success page.

And when you go to dynamodb and check for data. You will find the user data in there.


And that’s all for this blog. I hope you enjoyed it and found it useful. Also if you have any doubts please drop a message on our discord server. The link can be found below.

👉 And I’ll see you in next one, till then bye-bye and take care.