Building a Serverless Visitor Counter with Lambda and DynamoDB
Part 7 of the AWS Cloud Resume Challenge.
With the frontend of the website fully secured and cost monitoring configured, the next step was adding the first dynamic feature to the project: a visitor counter.
Backend source for this feature lives in Cloud-Resume-Backend. The live HTTP endpoint is:
GET https://lqwo3tc3q5.execute-api.us-east-1.amazonaws.com/prod/api/visitor
The site calls that API from src/js/visitor-counter.js and renders the returned views value.
This feature is a core component of the AWS Cloud Resume Challenge because it demonstrates how data flows through a modern cloud architecture. A request begins in the user's browser, triggers backend logic, updates a database, and returns a result to the frontend.
To build this feature, I implemented a serverless architecture using the following AWS services:>- Amazon DynamoDB to store the visitor count
- AWS Lambda to process the request and update the database
Architecture Overview
The visitor counter uses a simple serverless workflow:
flowchart TD browser[User Browser] --> apigw[API Gateway] apigw --> lambda[AWS Lambda] lambda --> ddb[(DynamoDB)] ddb --> lambda lambda --> apigw apigw --> browser
Each new browser session triggers a request that increments the visitor count stored in DynamoDB (page navigations within the same session do not).
Step 1 - Setting Up the Database (DynamoDB)
The first step was creating a place to store the visitor count. For this I used Amazon DynamoDB, AWS's fully managed NoSQL database service.
DynamoDB is designed for high availability and low latency, making it well suited for lightweight application data such as a visitor counter. I created a table called:cloud-resume-stats
The table was configured with the following key:
- Partition Key:
id(String)
id: visitorscount: 0
Step 2 - Writing the Backend Logic (AWS Lambda)
Next I created an AWS Lambda function responsible for updating the visitor count.
Lambda allows code to run in response to events without provisioning or managing servers. The function executes only when triggered, making it efficient and cost effective for small workloads like this. The function was written in Python using theboto3 library, which is the AWS SDK for Python.
The Lambda function performs three main tasks:
- Connect to the DynamoDB service
- Increment the visitor count stored in the table
- Return the updated value to the caller
update_item operation with an update expression.
This allows DynamoDB to handle the increment operation safely.
Why this matters: if multiple users load the website at the same time, DynamoDB ensures the counter increments correctly without race conditions or lost updates.
Step 3 - Securing the Connection with IAM
By default, a Lambda function cannot access other AWS services. Permissions must be explicitly granted through an IAM role.
Each Lambda function runs with an execution role that defines what resources the function can interact with. To follow the Principle of Least Privilege, I created a custom IAM policy granting only the permissions required for the visitor counter.The policy allows the following actions:
dynamodb:GetItemdynamodb:UpdateItem
After attaching the policy to the Lambda execution role, I tested the function using the Lambda test feature to confirm it could successfully update the database.
Step 4 - Creating an Endpoint for the Frontend
In order for the website to trigger the Lambda function, the browser needs a public HTTP endpoint. Instead of exposing the Lambda function directly, I created an endpoint using Amazon API Gateway. API Gateway acts as the entry point for HTTP requests and forwards those requests to the Lambda function.
Using an HTTP API in API Gateway provides a clean and scalable way for frontend applications to communicate with serverless backend services. When a visitor loads the website, the browser sends a request to the API Gateway endpoint. API Gateway then triggers the Lambda function, which updates the visitor counter stored in DynamoDB and returns the updated value to the browser.
Because the website is hosted on a different domain than the API endpoint, I configured CORS (Cross Origin Resource Sharing). CORS allows browsers to safely make requests between different domains. To restrict access and follow security best practices, the API endpoint was configured with the following settings:
- Allowed Method: GET
- Allowed Origin: https://khansaiful.com
Step 5 - Integrating the Counter with the Frontend
With the backend complete, the final step was connecting the visitor counter to the website.
I added a small JavaScript script that runs when the page loads.
The script performs three actions:
- Sends a GET request to the API Gateway
/prod/api/visitorendpoint - Waits for the JSON response containing the updated visitor count
- Updates the visitor counter displayed on the page
- The browser sends a request to API Gateway
- Lambda increments the value stored in DynamoDB
- The updated visitor count is returned to the browser
- The page displays the updated value
Result
The website now includes a fully functional serverless visitor counter.
The final architecture looks like this:
flowchart TD browser[User Browser] --> apigw[API Gateway] apigw --> lambda[AWS Lambda] lambda --> ddb[(DynamoDB)] ddb --> lambda lambda --> apigw apigw --> ui[Updated visitor count on page]
Each time a new browser session loads the site, the counter increments automatically.
What Is Next
Up to this point most of the infrastructure for this project was created manually through the AWS console. This approach is often referred to as ClickOps.
While this method works well for learning, production environments rely on Infrastructure as Code to ensure infrastructure is reproducible, version controlled, and easier to maintain.
In the next stage of the project I am converting this infrastructure into Terraform. That work is in progress: the visitor counter already runs via API Gateway, Lambda, and DynamoDB, and Terraform will take over provisioning those resources as code.
Next Up: Infrastructure as Code - Rebuilding the Project with Terraform







