DEV Community

Cover image for Build a Three-Tier Web App
Hyelngtil Isaac
Hyelngtil Isaac

Posted on • Originally published at hyelngtil.awstech

Build a Three-Tier Web App

Introducing Today's Project!

In this project, I will demonstrate how to architect and deploy a serverless three-tier application by hosting static web assets in Amazon S3 delivered globally via Amazon CloudFront, executing business logic using AWS Lambda routed through Amazon API Gateway, and managing persistence with Amazon DynamoDB.

I'm doing this project to demonstrate end-to-end integration of serverless AWS components, cross-origin resource sharing (CORS) configurations, least-privilege IAM policies, and cloud troubleshooting practices.

Tools and concepts

Services I used were Amazon S3, Amazon CloudFront, AWS Lambda, Amazon API Gateway, and Amazon DynamoDB. Key concepts I learnt include Lambda functions, serverless three-tier architecture, API REST routing, Cross-Origin Resource Sharing (CORS) configurations, Origin Access Control (OAC), and NoSQL partition key database management.


Project reflection

This project took me approximately 150 minutes. The most challenging part was debugging the Cross-Origin Resource Sharing (CORS) restrictions between the CloudFront distribution and the API Gateway. It was most rewarding to successfully deploy a decoupled, serverless three-tier architecture while enforcing secure HTTP routing and least-privilege IAM policies.

I chose to do this project today because validating secure, decoupled serverless architectures and enforcing strict IAM policies perfectly aligns with my core focus on cloud engineering and cybersecurity.


Presentation tier

For the presentation tier, I will set up an Amazon S3 bucket to store static frontend assets (index.html, style.css, script.js) and an Amazon CloudFront CDN distribution configured with Origin Access Control (OAC) because this provides scalable, low-latency global web content delivery while securely restricting direct public access to the underlying storage bucket.

I accessed my delivered website by requesting the domain name assigned to the CloudFront distribution via HTTPS, which securely retrieved and served the cached static frontend files from edge locations using Origin Access Control permissions to query the origin S3 bucket.

Image


Logic tier

For the logic tier, I will set up AWS Lambda functions and an Amazon API Gateway REST API because API Gateway acts as the secure entry point for HTTP requests from the frontend, routing them to Lambda to execute serverless backend business logic and process application data without managing underlying server infrastructure.

The Lambda function retrieves data by parsing the userId query parameter from the incoming HTTP request event and using the AWS SDK DynamoDB Document Client (GetCommand) to execute a read request against the target DynamoDB table (UserData).


Data tier

For the data tier, I will set up an Amazon DynamoDB NoSQL table named UserData with a primary partition key of userId because it provides a fully managed, high-performance, and schemaless storage layer for persistent application data that scales seamlessly without server management overhead.

The partition key for my DynamoDB table is userId, which means DynamoDB uses this unique attribute value to hash and route data items across underlying physical partitions, enabling efficient, single-digit millisecond key-value lookups and persistent storage for application user records.

Image


Logic and Data tier

Once all three layers of my three-tier architecture are set up, the next step is to integrate the presentation layer with the logic tier by updating script.js to target the API Gateway Invoke URL because this establishes end-to-end communication, allowing the frontend to trigger Lambda functions and retrieve DynamoDB data.

To test my API, I appended the /users?userId=1 path and query string parameter directly to the API Gateway prod stage Invoke URL and executed the HTTP GET request in a web browser. The results were successful, returning a 200 OK HTTP response containing the JSON payload from the DynamoDB table.

Image


Console Errors

The error in my distributed site was because the frontend script.js file contained an unresolved placeholder ([YOUR-PROD-API-URL]) instead of the active Amazon API Gateway Invoke URL, preventing the client browser from successfully routing HTTP requests to the backend logic tier.

To resolve the error, I updated script.js by replacing the [YOUR-PROD-API-URL] placeholder with the active API Gateway prod stage Invoke URL. I then reuploaded it into S3 because CloudFront caches and serves frontend assets from this origin bucket, and this update ensures client browsers receive the correct endpoint to properly route HTTP requests to the backend logic tier.

I ran into a second error after updating script.js. This was an error with Cross-Origin Resource Sharing (CORS) because the Amazon API Gateway was not configured to explicitly allow HTTP requests from the CloudFront distribution domain, causing the client browser to block the cross-origin request.

Image


Resolving CORS Errors

To resolve the CORS error, I first enabled CORS on the Amazon API Gateway by selecting both GET and OPTIONS under Access-Control-Allow-Methods. Next, I set the Access-Control-Allow-Origin value to my specific CloudFront distribution domain name. Finally, I redeployed the REST API to the production stage to ensure the new security configuration took effect.

I also updated my Lambda function because the Amazon API Gateway uses Lambda Proxy Integration, requiring the backend function itself to explicitly construct and return the full HTTP response, including security headers. The changes I made were injecting the Access-Control-Allow-Origin header configured with the CloudFront distribution domain into the JSON response objects across all status codes to securely permit cross-origin browser requests.

Image


Fixed Solution

I verified the fixed connection between API Gateway and CloudFront by reloading the CloudFront distribution domain in the client browser and validating that the frontend successfully executed the cross-origin HTTP GET request to retrieve and render the JSON payload from the DynamoDB table.

Image


🀝This is the only project in the series on Three tier Web App.

Next series will be on Kubernetes


Top comments (0)