This project is a serverless backend service designed to track and display real-time visitor analytics. Unlike simple static counters, this application leverages Cloud-Native architecture to handle concurrent traffic and data persistence without managing servers.
The core functionality relies on AWS Lambda for compute and Amazon DynamoDB for atomic data storage, ensuring accuracy and high availability.
The system uses a completely serverless event-driven architecture:
graph LR
Client(Web Client) -- HTTPS/Fetch --> API(Lambda Function URL)
API -- Trigger --> Lambda(AWS Lambda\nPython Backend)
Lambda -- Atomic Update --> DB[(Amazon DynamoDB\nNoSQL Database)]
DB -- Return New Count --> Lambda
Lambda -- JSON Response --> Client
- Trigger: A frontend client (static website) sends an asynchronous
fetchrequest to the public API endpoint. - Compute: The request triggers an AWS Lambda function written in Python.
- Persistence: The function connects to DynamoDB and performs an atomic increment operation. This ensures that if two users visit at the exact same millisecond, both visits are counted correctly (preventing race conditions).
- Response: The updated count is returned to the client as a JSON object.
| Component | Technology | Role |
|---|---|---|
| Compute | AWS Lambda | Serverless execution of backend logic |
| Database | Amazon DynamoDB | NoSQL database for low-latency storage |
| Language | Python 3.12 | Logic implementation using boto3 SDK |
| API | Function URL | Public HTTPS endpoint for the Lambda function |
| Security | AWS IAM | Role-based access control (RBAC) |
| Client | JavaScript (ES6) | Frontend integration via Fetch API |
- Table Name:
VisitorCount - Primary Key:
id(String) - Item Structure:
{ "id": "main_page", "views": 42 }
The core logic resides in lambda_function.py. It uses the boto3 library to interact with DynamoDB.
Key Feature - Atomic Counter:
Instead of reading the value, adding 1, and writing it back (which causes race conditions), I utilized DynamoDB's set #v = #v + :incr update expression. This happens entirely on the database side for maximum speed and accuracy.
import json
import boto3
from decimal import Decimal
# Initialize DynamoDB Resource
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('VisitorCount')
# Custom Encoder to handle DynamoDB Decimal format
class DecimalEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return int(obj)
return super(DecimalEncoder, self).default(obj)
def lambda_handler(event, context):
# Atomic update to prevent race conditions
response = table.update_item(
Key={'id': 'main_page'},
UpdateExpression='SET #v = #v + :incr',
ExpressionAttributeNames={'#v': 'views'},
ExpressionAttributeValues={':incr': 1},
ReturnValues="UPDATED_NEW"
)
view_count = response['Attributes']['views']
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*' # CORS Configuration
},
'body': json.dumps({'count': view_count}, cls=DecimalEncoder)
}Security is handled via AWS IAM (Identity and Access Management) to ensure the Principle of Least Privilege:
- IAM Execution Role: The Lambda function operates under a custom role (
UpdateVisitorCount-role) that has permissions strictly limited to reading and writing to theVisitorCountDynamoDB table. It cannot access S3, EC2, or other unrelated services. - CORS Configuration: The Function URL is configured with Cross-Origin Resource Sharing (CORS) to allow requests from the portfolio domain, while blocking unauthorized cross-site scripts.
Problem: The CORS Error
- Issue: When fetching the API from the frontend, the browser blocked the request because the API and the website were on different domains.
- Solution: I configured the Lambda Function URL to return the header
Access-Control-Allow-Origin: *and enabled CORS in the AWS Console settings forGETmethods.
Problem: Data Type Serialization
- Issue: DynamoDB returns numbers as
Decimaltypes, which Python's standardjson.dumpscannot serialize. - Solution: Implemented a custom
DecimalEncoderclass to convertDecimaltypes to standard Pythonintorfloatbefore returning the JSON response.
- Clone the repository.
- Install
boto3:pip install boto3 - Configure AWS Credentials:
aws configure - Run the python script (requires a live DynamoDB table connection).