Photo by Vienna Reyes on Unsplash
🌤️ Day 2: Building a Real-Time NBA Game Day Notification System with AWS Lambda, SNS and EventBridge (Event-driven Serverless Architecture)
30 Day DevOps Challenge | DevOpsAllStarsChallenge
Get ready to build your own automated NBA game notification system! This guide will walk you through setting up a local Python environment and integrating powerful AWS serverless tools like Lambda, SNS, and EventBridge with the SportsData.io API. By the end, you’ll have a dynamic service that delivers live game alerts straight to your users.
Check out the project on GitHub: AWSGameDayNotificationService
Introduction
Ever found yourself missing the big moments in an NBA game? Maybe you were stuck in a meeting, out with friends, or just didn’t have time to check the scores. I’ve been there too, and that’s exactly why I built the Game Day Alerts application. It’s like having your own personal courtside assistant—fetching live NBA game data from SportsData.io and sending you instant notifications via email or SMS. Powered by AWS services like Lambda and EventBridge, this event-driven solution ensures you never miss a clutch play or buzzer-beater again.
Prerequisites
To build the Game Day Notification system, you’ll need the following tools and resources. If you’ve already installed them, feel free to skip to the next section.
1. Python 3.x
This is the programming language we’ll use to write the Lambda function code.
- Install it: Download Python 3.x from python.org, run the installer, and check "Add Python to PATH" during installation.
2. pip
pip
is Python's package manager, essential for installing dependencies.
Verify it’s installed: Open your terminal and run:
pip --version
Missing pip? Install it:
python -m ensurepip --upgrade
3. AWS CLI
The AWS CLI allows you to manage AWS resources via the command line.
Install it: Download the AWS CLI for your system, follow the setup instructions, and verify with:
aws --version
4. VS Code
A lightweight yet powerful code editor with Python support.
- Install it: Download Visual Studio Code from code.visualstudio.com and follow the setup steps.
5. SportsData.io API Key
This key is required to access SportsData.io’s services for fetching game day information.
Visit SportsDataIO and sign up
Subscribe to the NBA API Free Trial to gain access
Copy the API Key (e.g.,
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
), which will be used in your Lambda function.Store it Securely until we need later on.
With these tools installed, you're ready to start building your Game Day Alerts application!
System Design
Core Process Flow:
Lets dive into each Component:
Local Computer:
Here we will create the Python code for the Lambda function and a CloudFormation template to automate infrastructure deployment. These files are prepared locally before being uploaded to AWS.
AWS S3 Bucket:
Simple Storage (S3) is a scalable storage service for hosting files and data, this data is called an Object and is stored in a container called a Bucket. The zipped Python code and CloudFormation template will be uploaded here for easy access during deployment.
AWS Simple Notification Service (SNS):
SNS is a messaging service for sending notifications to users via email, SMS, or other methods. Here, we will use SNS to create a topic and subscriptions, enabling the system to route game score updates to end users.
AWS Identity and Access Management (IAM):
IAM is a security service that controls permissions for AWS resources and services. We will configure roles and policies in IAM to ensure the Lambda function and EventBridge have the necessary permissions to function securely.
AWS EventBridge:
This is a service that delivers events from various sources and triggers actions based on rules. This is the service that schedules or triggers the Lambda function, ensuring it runs at specific times or in response to defined events.
AWS Lambda:
Lambda is a serverless compute service that runs code without needing to manage servers. The Lambda function will execute the uploaded Python code, process the game score data, and interact with other services like SNS.
AWS CloudWatch Logs:
This is a monitoring and logging service for AWS resources. It will log Lambda execution details, track performance, debug issues, and optimize the function.
Let’s get Started Building
There are 5 main steps to building this application, don’t worry - we will go through each step in detail so you have enough information to complete this on your own.
Creating an SNS Topic and Subscription (and confirming this Subscription
Preparing Python Lambda Code and CloudFormation Template
Uploading Lambda Code and CloudFormation Template to S3
Deploying the CloudFormation Stack
Testing and Validation
Creating a SNS Topic and Subscription
Create an SNS Topic:
Go to the Amazon SNS Console.
Click "Create topic", select "Standard", and name the topic (e.g.,
GameDayNotificationTopic
).Click "Create topic" to finalize.
Create a Subscription:
Select your topic and click "Create subscription".
Choose a protocol (e.g., Email, SMS, Lambda, HTTP/HTTPS) and provide the endpoint (e.g., email address or phone number).
Confirm the subscription via the endpoint if required (e.g., by clicking a confirmation link in an email).
Test the Setup:
- Publish a test message from the SNS Console to verify the subscription.
Creating the Python Lambda Code
import os
import json
import urllib.request
import boto3
from datetime import datetime, timedelta, timezone
def format_game_data(game):
status = game.get("Status", "Unknown")
away_team = game.get("AwayTeam", "Unknown")
home_team = game.get("HomeTeam", "Unknown")
final_score = f"{game.get('AwayTeamScore', 'N/A')}-{game.get('HomeTeamScore', 'N/A')}"
start_time = game.get("DateTime", "Unknown")
channel = game.get("Channel", "Unknown")
# Format quarters
quarters = game.get("Quarters", [])
quarter_scores = ', '.join([f"Q{q['Number']}: {q.get('AwayScore', 'N/A')}-{q.get('HomeScore', 'N/A')}" for q in quarters])
if status == "Final":
return (
f"Game Status: {status}\n"
f"{away_team} vs {home_team}\n"
f"Final Score: {final_score}\n"
f"Start Time: {start_time}\n"
f"Channel: {channel}\n"
f"Quarter Scores: {quarter_scores}\n"
)
elif status == "InProgress":
last_play = game.get("LastPlay", "N/A")
return (
f"Game Status: {status}\n"
f"{away_team} vs {home_team}\n"
f"Current Score: {final_score}\n"
f"Last Play: {last_play}\n"
f"Channel: {channel}\n"
)
elif status == "Scheduled":
return (
f"Game Status: {status}\n"
f"{away_team} vs {home_team}\n"
f"Start Time: {start_time}\n"
f"Channel: {channel}\n"
)
else:
return (
f"Game Status: {status}\n"
f"{away_team} vs {home_team}\n"
f"Details are unavailable at the moment.\n"
)
def lambda_handler(event, context):
# Get environment variables
api_key = os.getenv("NBA_API_KEY")
sns_topic_arn = os.getenv("SNS_TOPIC_ARN")
sns_client = boto3.client("sns")
# Adjust for Central Time (UTC-6)
utc_now = datetime.now(timezone.utc)
central_time = utc_now - timedelta(hours=6) # Central Time is UTC-6
today_date = central_time.strftime("%Y-%m-%d")
print(f"Fetching games for date: {today_date}")
# Fetch data from the API
api_url = f"https://api.sportsdata.io/v3/nba/scores/json/GamesByDate/{today_date}?key={api_key}"
print(today_date)
try:
with urllib.request.urlopen(api_url) as response:
data = json.loads(response.read().decode())
print(json.dumps(data, indent=4)) # Debugging: log the raw data
except Exception as e:
print(f"Error fetching data from API: {e}")
return {"statusCode": 500, "body": "Error fetching data"}
# Include all games (final, in-progress, and scheduled)
messages = [format_game_data(game) for game in data]
final_message = "\n---\n".join(messages) if messages else "No games available for today."
# Publish to SNS
try:
sns_client.publish(
TopicArn=sns_topic_arn,
Message=final_message,
Subject="NBA Game Updates"
)
print("Message published to SNS successfully.")
except Exception as e:
print(f"Error publishing to SNS: {e}")
return {"statusCode": 500, "body": "Error publishing to SNS"}
return {"statusCode": 200, "body": "Data processed and sent to SNS"}
Creating the CloudFormation Template
AWSTemplateFormatVersion: '2010-09-09' # Version of CloudFormation template
Description: Game Day Notification System using AWS
Resources:
# IAM Policy for Lambda Execution Role
GameDayLambdaExecutionRolePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: "GameDayLambdaPublishSNSPolicy"
Roles:
- !Ref GameDayLambdaExecutionRole
PolicyDocument:
Version: "2012-10-17" # Standard version for IAM policy documents
Statement:
- Effect: "Allow" # Defines the permissions
Action:
- "sns:Publish" # Allows Lambda to publish messages to the SNS topic
Resource: "arn:aws:sns:REGION:ACCOUNT_ID:TOPIC_NAME" # Replace with your SNS topic ARN
# IAM Role for Lambda Execution
GameDayLambdaExecutionRole:
Type: AWS::IAM::Role # IAM role that the Lambda function will assume
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "sts:AssumeRole"
Principal:
Service: "lambda.amazonaws.com" # Defines who or what can assume this role
Policies:
- PolicyName: "AWSLambdaBasicExecutionRole" # AWS-managed policy for CloudWatch Logs
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "logs:*"
Resource: "arn:aws:logs:*:*:*"
- PolicyName: "GameDayLambdaPublishSNSPolicy" # Custom policy for SNS Publish
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "sns:Publish"
Resource: "arn:aws:sns:REGION:ACCOUNT_ID:TOPIC_NAME" # Replace with your SNS topic ARN
# Lambda Function
GameDayLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: "GameDayNotificationFunction"
Handler: "lambda_function.lambda_handler" # Lambda handler function
Role: !GetAtt GameDayLambdaExecutionRole.Arn
Runtime: "python3.8"
Timeout: 60
MemorySize: 128
Environment:
Variables:
SPORTS_API_KEY: "API_KEY_PLACEHOLDER" # Replace with actual value
SNS_TOPIC_ARN: "arn:aws:sns:REGION:ACCOUNT_ID:TOPIC_NAME" # Replace with your SNS topic ARN
Code:
S3Bucket: "S3_BUCKET_NAME" # Replace with actual S3 bucket name
S3Key: "lambda_code.zip" # Replace with actual code location in the bucket
# IAM Role for EventBridge to Invoke Lambda
EventBridgeLambdaInvokeRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "events.amazonaws.com" # EventBridge as principal
Action: "sts:AssumeRole"
Policies:
- PolicyName: "EventBridgeLambdaInvokePolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "lambda:InvokeFunction"
Resource: !GetAtt GameDayLambdaFunction.Arn # Dynamically reference Lambda ARN
# EventBridge Rule
GameDayNotificationScheduleRule:
Type: AWS::Events::Rule
Properties:
Name: "GameDayNotificationScheduleRule"
Description: "Trigger Lambda for game day notifications based on schedule"
ScheduleExpression: "cron(0 9-23/2 * * ? *)" # Every 2 hours between 9 AM and 11 PM
State: "ENABLED"
Targets:
- Arn: !GetAtt GameDayLambdaFunction.Arn # Dynamically reference Lambda ARN
Id: "GameDayNotificationFunctionTarget"
InputTransformer:
InputPathsMap:
currentDate: "$.time" # Use the built-in 'time' field to pass the current time
InputTemplate: '{"date": "${aws:now()}" }' # Pass the current date as 'date'
# Lambda Permission for EventBridge
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !Ref GameDayLambdaFunction # Dynamically reference Lambda function
Principal: "events.amazonaws.com"
SourceArn: !GetAtt GameDayNotificationScheduleRule.Arn # Reference EventBridge Rule ARN
# CloudWatch Log Group for Lambda
GameDayLambdaLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${GameDayLambdaFunction}" # Explicit Log Group for Lambda
RetentionInDays: 7 # Retain logs for 7 days for cost control and monitoring
Outputs: # Outputs for CloudFormation stack
LambdaFunctionArn:
Description: "ARN of the Lambda function"
Value: !GetAtt GameDayLambdaFunction.Arn
SnsTopicArn:
Description: "ARN of the SNS topic"
Value: "arn:aws:sns:REGION:ACCOUNT_ID:TOPIC_NAME" # Replace with your SNS topic ARN
Key Considerations:
Replace placeholder values
Ensure the
S3Bucket
andS3Key
point to your correct S3 URIMake sure your template matches the name of your Lambda Function, SNS Topic etc
Uploading Lambda Code and CloudFormation Template to S3
Prepare the Files:
Lambda Code: Ensure your Python code is zipped (e.g.,
lambda_
code.zip
).CloudFormation Template: Save your YAML or JSON template file (e.g.,
template.yaml
).
Log In to the AWS Management Console:
- Navigate to the Amazon S3 Console.
Create or Select an S3 Bucket:
If needed, create a new bucket:
Click "Create bucket" and provide a unique bucket name (e.g.,
gamedaybucket
).Configure region and settings as required.
Open your desired bucket.
Upload the Files:
Click "Upload" and then "Add files".
Select
lambda_code.zip
andtemplate.yaml
from your local system.Click "Upload" to finalize.
Verify the Upload:
Ensure both files are listed in the bucket.
Note the Bucket Name and S3 URI Path (e.g.,
my-bucket-name/path/to/lambda_code.zip
).
Deploying the CloudFormation Stack
Go to the CloudFormation Console:
Open the AWS Management Console and navigate to CloudFormation.
Click Create Stack → With new resources (standard).
Upload the CloudFormation Template:
- Choose Upload a template file and select your updated YAML file (e.g.,
game_day_notification.yaml
).
- Choose Upload a template file and select your updated YAML file (e.g.,
Enter Parameters:
- Provide any necessary parameters like your email address for SNS.
Configure Stack Options:
- Specify stack name and any other optional settings.
Review and Create:
- Review your setup and click Create Stack to deploy the resources.
Your stack will have the CREATE_COMPLETE once successful.
Adding Environment Variables to Your Lambda Function
After creating your CloudFormation stack, follow these steps to configure environment variables for your Lambda function directly in the AWS Management Console:
Identify Required Environment Variables:
Review your Lambda function code and note any variables it depends on, such as:
SPORTS_API_KEY
SNS_TOPIC_ARN
Access the Lambda Function in AWS:
Go to the AWS Lambda Console.
Locate your function by searching for its name (e.g.,
GameDayNotificationFunction
).
Add Environment Variables:
Click on your function to open its configuration page.
In the left-hand menu, click "Configuration", then "Environment variables".
Click "Edit", then "Add environment variable".
Enter the key-value pairs for your environment variables, for example:
Key:
SPORTS_API_KEY
, Value:your_sportsdata_api_key
Key:
SNS_TOPIC_ARN
, Value:arn:aws:sns:region:account-id:topic-name
Click "Save".
Testing and Validation
Manually Invoke Lambda (Optional):
In the Lambda Console, select your Lambda function and click Test.
Create a test event (e.g.,
{ "date": "2025-01-08" }
) to verify that the function sends notifications correctly.
Verify Scheduled Invocation:
- If you have configured EventBridge to trigger the Lambda function on a schedule, check that it runs according to your cron settings.
Check CloudWatch Logs:
- Open CloudWatch Logs to validate Lambda’s execution.
Once Lambda has been invoked by Eventbridge, you should get your notification:
Challenges and Solutions
Throughout the process, I encountered several challenges. Here’s how I solved them:
EventBridge Rule Errors:
Issue: The deployment failed due to a misconfigured EventBridge rule.
Solution: Removed the
RoleArn
property from the CloudFormation template, as EventBridge automatically uses the Lambda execution role.
!GetAtt vs. !Ref Confusion:
Issue: I initially struggled to understand when to use
!GetAtt
versus!Ref
in CloudFormation.Solution: I learned to use
!Ref
for resource names and!GetAtt
when specific attributes like ARNs are required.
Learning CloudFormation:
Issue: Writing the YAML file for CloudFormation was a bit challenging at first.
Solution: With some practice, I became more comfortable with CloudFormation syntax and troubleshooting common errors.
Conclusion and Next Steps
Recap:
This application enables real-time notifications to email/SMS for NBA game day alerts using AWS services.
We’ve learned how to set up AWS Lambda, SNS, and CloudFormation for a Serverless Event-driven system.
Automated deployment via CloudFormation and Built-in monitoring through CloudWatch.
Next Steps:
Explore integrating other sports APIs or adding additional notification channels (e.g., SMS).
Consider adding user authentication to personalize notifications.
Key Takeaways:
Automation and serverless architecture save time and reduce complexity.
Testing and troubleshooting are essential for success in AWS.
Congratulations! You’ve successfully built a fully automated Game Day Notification system using AWS services.
But this is just the beginning! Stay tuned for the next project, where we’ll take things to the next level. I’ve found that the possibilities with AWS are endless, and each project is a step toward mastering cloud-based development. Until next time, keep coding, keep innovating, and get ready for more exciting challenges ahead!