A production-style serverless URL shortener built on AWS.
Paste a long URL, get a short one back.
Built to explore real-world AWS architecture using serverless services, Infrastructure as Code with CloudFormation, automated deployments with GitHub Actions, observability with CloudWatch, API protection, and scalable event-driven design.
- AWS Lambda β serverless backend functions
- AWS API Gateway (HTTP API) β public HTTP endpoints
- AWS DynamoDB β NoSQL database
- AWS S3 β static frontend hosting
- AWS CloudFormation β infrastructure as code
- GitHub Actions β CI/CD pipeline
- AWS CloudWatch β dashboards, metrics, alarms
- AWS SNS β email alerts for failures
- Python 3.12 β Lambda runtime
- Pytest β unit testing
User visits /xK3p9
β
βΌ
API Gateway
β
βΌ
Lambda (handler.py)
β
βΌ
DynamoDB β finds slug
β
βΌ
Returns HTTP 301 redirect
β
βββ asynchronously invokes analytics Lambda
POST /links { "url": "https://long-url.com" }
β
βΌ
API Gateway
β
βΌ
Lambda Authorizer validates x-api-key
β
βΌ
Main Lambda validates payload
β
βΌ
Generates random slug + stores in DynamoDB
β
βΌ
Returns short URL
Redirect request
β
βΌ
handler.py
β
βΌ
Invokes analytics Lambda asynchronously
β
βΌ
analytics.py stores click event in clicks table
POST /links is protected by a Lambda Authorizer that validates the x-api-key header before the request reaches the main Lambda. Without a valid key the request is blocked with 401 Unauthorized.
The backend validates all incoming payloads:
- Payload size limit (4096 bytes)
- Invalid JSON
- Missing or non-string URL
- URL length limit (2048 chars)
- Invalid protocol (must be http:// or https://)
Each Lambda has its own IAM role with only the permissions it needs:
| Lambda | Permissions |
|---|---|
link-shortener |
Read/write links table, invoke analytics Lambda, write logs |
link-analytics |
Write clicks table, write logs |
link-authorizer |
Write logs only |
No role has access to resources it doesn't use.
Automatically created with four panels:
- Lambda invocations
- Lambda errors
- Lambda duration
- Analytics invocations (click tracking)
CloudWatch alarm monitors Lambda errors. If errors exceed the threshold, SNS sends an email notification.
DynamoDB TTL automatically removes old records:
- Links expire after 30 days
- Click analytics expire after 90 days
No scheduled jobs or manual cleanup needed.
All resources are defined in template.yaml using CloudFormation.
CloudFormation generates resource names automatically β no hardcoded names. The pipeline reads actual resource names from stack Outputs after deployment and uses them dynamically.
| Resource | Description |
|---|---|
LinksTable |
Stores short links with TTL |
ClicksTable |
Stores click analytics with TTL |
LambdaFunction |
Main URL shortener Lambda |
AnalyticsFunction |
Click tracking Lambda |
AuthorizerFunction |
API key validator Lambda |
ApiGateway |
HTTP API with CORS |
ApiAuthorizer |
Lambda authorizer for POST route |
RoutePost |
Protected POST /links |
RouteGet |
Public GET /{id} redirect |
AlertTopic |
SNS topic for failure alerts |
ErrorAlarm |
CloudWatch alarm |
Dashboard |
CloudWatch metrics dashboard |
link-shortener/
βββ lambda/
β βββ handler.py # Main Lambda β redirect and create logic
β βββ analytics.py # Analytics Lambda β click tracking
β βββ authorizer.py # Authorizer Lambda β API key validation
β βββ test_handler.py # Unit tests for handler
β βββ test_authorizer.py # Unit tests for authorizer
βββ frontend/
β βββ index.html # Static UI hosted on S3
βββ .github/
β βββ workflows/
β βββ deploy.yml # CI/CD pipeline
βββ template.yaml # CloudFormation β all AWS resources
βββ README.md
Every push to main triggers the pipeline automatically.
- Run tests β pytest with mocked DynamoDB/Lambda, no AWS connection required
- Deploy CloudFormation β creates or updates all AWS resources
- Read resource names from Outputs β pipeline fetches actual Lambda names dynamically
- Deploy Lambda code β packages and uploads handler, analytics, authorizer
- Inject frontend variables β API URL and API key injected via
sed - Deploy frontend to S3 β syncs static files to S3 website hosting
- AWS account (free tier is sufficient)
- GitHub repository
| Secret | Description |
|---|---|
AWS_ACCESS_KEY_ID |
IAM user access key |
AWS_SECRET_ACCESS_KEY |
IAM user secret key |
AWS_REGION |
Target region (e.g. us-east-2) |
AWS_ACCOUNT_ID |
12-digit AWS account ID |
ALERT_EMAIL |
Email for CloudWatch failure alerts |
API_KEY |
Secret key to protect POST /links |
Push to main. GitHub Actions handles everything automatically.
Frontend available at:
http://link-shortener-{AWS_ACCOUNT_ID}.s3-website.{AWS_REGION}.amazonaws.com
pip install pytest boto3
pytest lambda/test_handler.py lambda/test_authorizer.py -vAll DynamoDB and Lambda calls are mocked β no AWS connection required.
Built to:
- Work hands-on with core AWS services β Lambda, API Gateway, DynamoDB, S3, IAM, SNS, CloudWatch
- Practice Infrastructure as Code with CloudFormation
- Build a CI/CD pipeline that gates deploys behind automated tests
- Understand serverless architecture and event-driven design
- Apply security principles β least privilege IAM, API key protection, input validation
- Implement observability β dashboards, alarms, and alerting