As part of the Cloud Resume Challenge, I needed to implement a visitor counter that would track and display the number of visitors to my resume website. Here's how I built it using AWS serverless services.
The Architecture
The visitor counter consists of three main AWS services working together: API Gateway receives HTTP requests from the frontend, Lambda processes the business logic, and DynamoDB stores the counter value persistently.
Why Serverless?
I chose a serverless architecture for several reasons:
- Cost efficiency: Pay only for what you use—perfect for a low-traffic portfolio site
- Zero server management: No patching, scaling, or maintenance required
- Automatic scaling: Handles traffic spikes without configuration
- High availability: Built-in redundancy across availability zones
The Lambda Function
The core logic lives in a Python Lambda function. The key challenge was implementing an atomic counter increment to avoid race conditions when multiple visitors hit the site simultaneously.
response = dynamodb.update_item(
TableName=table_name,
Key={'visitor_count': {'S': 'main'}},
UpdateExpression='ADD #count :inc',
ExpressionAttributeNames={'#count': 'count'},
ExpressionAttributeValues={':inc': {'N': '1'}},
ReturnValues='ALL_NEW'
)
Using DynamoDB's ADD operation ensures atomicity—the increment happens in a single operation at the database level, eliminating race conditions.
Handling CORS
One of the trickier aspects was configuring CORS correctly. The frontend runs on a different domain than the API, so proper headers are essential:
'headers': {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'GET,OPTIONS'
}
Lessons Learned
Building this seemingly simple feature taught me several valuable lessons:
- DynamoDB has reserved words (like "count") that require ExpressionAttributeNames
- The boto3 client API requires explicit type descriptors for all values
- API Gateway OPTIONS method is crucial for CORS preflight requests
- Always test your Lambda functions locally before deploying
What's Next
In my next post, I'll cover how I automated the deployment of this infrastructure using Terraform and GitHub Actions for continuous delivery.