🌤️ 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

·

11 min read

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.

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:

đź’ˇ
EventBridge (Trigger) → Lambda (Processing) → SNS (Distribution) → User Notifications

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.

  1. Creating an SNS Topic and Subscription (and confirming this Subscription

  2. Preparing Python Lambda Code and CloudFormation Template

  3. Uploading Lambda Code and CloudFormation Template to S3

  4. Deploying the CloudFormation Stack

  5. 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 and S3Key point to your correct S3 URI

  • Make sure your template matches the name of your Lambda Function, SNS Topic etc


Uploading Lambda Code and CloudFormation Template to S3

  1. 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).

  2. Log In to the AWS Management Console:

  3. 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.

  4. Upload the Files:

    • Click "Upload" and then "Add files".

    • Select lambda_code.zip and template.yaml from your local system.

    • Click "Upload" to finalize.

  5. 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

  1. Go to the CloudFormation Console:

    • Open the AWS Management Console and navigate to CloudFormation.

    • Click Create Stack → With new resources (standard).

  2. Upload the CloudFormation Template:

    • Choose Upload a template file and select your updated YAML file (e.g., game_day_notification.yaml).
  3. Enter Parameters:

    • Provide any necessary parameters like your email address for SNS.
  4. Configure Stack Options:

    • Specify stack name and any other optional settings.
  5. 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:

  1. Identify Required Environment Variables:

    • Review your Lambda function code and note any variables it depends on, such as:

      • SPORTS_API_KEY

      • SNS_TOPIC_ARN

  2. Access the Lambda Function in AWS:

  3. 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

  1. 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.

  2. 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.
  3. 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!

Did you find this article valuable?

Support Blessing's Coding Journey by becoming a sponsor. Any amount is appreciated!

Â