Master AWS with a Weather Alert Project

AWS serverless lambda dynamodb SES API

About 3 min read Ā· View the code on GitHub

On this page
Architecture of the AWS serverless weather alert system
Open full-size image

šŸ’» GitHub Repo: Check out the full source code here

Let’s Get Started

Let’s build something meaningful and scalable together—a production-grade serverless weather alert system on AWS. This isn’t just a ā€œfollow the stepsā€ tutorial—it’s us walking through smart decisions at every layer of this cloud-native solution. From ingestion to alerts, transformations to dashboards, we’ll explore every architectural building block in a way that’s fun and real.

This system is built using AWS best practices, keeping in mind the Well-Architected Framework, and it’s all fully reproducible using infrastructure as code.

šŸŒ What Are We Building?

We’re building a system where visitors can subscribe to get weather updates via email—daily forecasts and instant alerts for any severe conditions. Instead of asking users to manually select their country, we’ll use geolocation via ip-api.com based on their IP during signup. Our emails will be easy to unsubscribe from, thanks to secure tokenized links.

šŸ’Œ Let’s Handle Subscriptions

Our journey begins with a WordPress-embedded form. When someone subscribes, their details are sent to an API Gateway endpoint, which then triggers a Lambda function. This function extracts the email and automatically determines the user’s country using ip-api.com. All this info is then saved securely in a DynamoDB table.

🧩 CDK Snippet:

TypeScript
const subscriptionLambda = new lambda.Function(this, 'SubscriptionLambda', {
runtime: lambda.Runtime.PYTHON_3_11,
handler: 'subscription.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/subscription')),
environment: {
DDB_TABLE_NAME: table.tableName,
SENDER_EMAIL: senderEmail,
},
});

We chose DynamoDB over RDS and Aurora Serverless because:

  • It’s serverless and auto-scales
  • It has built-in high availability
  • It’s great for key-based lookups like checking if a user is subscribed

šŸ•’ Fetching Weather Data

The naive choice might be to use Kinesis for streaming—but we’re just polling a single public API (Open-Meteo) every 5 minutes. Kinesis would’ve added unnecessary complexity and cost.

Instead, we scheduled a Lambda using EventBridge to hit the API, parse the data, and store it as a raw JSON file in an S3 bucket.

🧩 CDK Snippet:

TypeScript
new events.Rule(this, 'WeatherFetchRule', {
schedule: events.Schedule.rate(Duration.minutes(5)),
targets: [new targets.LambdaFunction(weatherFetcherLambda)],
});

🧹 Processing Data with Glue

Now that we have raw JSON in S3, we transform it using AWS Glue. Our hourly Glue job:

  • Reads raw JSON
  • Converts it to partitioned Parquet
  • Writes output to a processed S3 bucket

🧩 CDK Snippet:

TypeScript
const glueJob = new glue.CfnJob(this, 'WeatherGlueJob', {
name: 'weather-transform-job',
role: glueJobRole.roleArn,
command: {
name: 'glueetl',
pythonVersion: '3',
scriptLocation: glueScriptAsset.s3ObjectUrl,
},
defaultArguments: {
'--enable-glue-datacatalog': 'true',
'--job-bookmark-option': 'job-bookmark-enable',
'--TempDir': `s3://${glueTempBucket.bucketName}/temp/`,
},
glueVersion: '4.0',
maxRetries: 1,
timeout: 10,
});

šŸ” Querying with Athena

Athena lets us query S3 like it’s a database. With Parquet files and partitioned data, Athena becomes incredibly efficient. The Glue job creates a schema in Glue Catalog that Athena uses.

🧩 CDK Snippet:

TypeScript
new glue.CfnDatabase(this, 'AthenaDatabase', {
catalogId: this.account,
databaseInput: { name: 'weather_data' },
});

šŸ“Š Visualizing with QuickSight

For dashboards, we connected Amazon QuickSight directly to Athena to display insights on:

  • Temperature
  • Precipitation
  • Alerts

We configured QuickSight via console since embedding requires domain validation and dashboard sharing configuration.

šŸ“¢ Let’s Send Some Alerts

We created two Lambda functions:

  • Daily digest
  • Severe weather alert

Each Lambda uses SES to send templated emails with unsubscribe links.

🧩 CDK Snippet:

TypeScript
const dailyAlertLambda = new lambda.Function(this, 'DailyAlertLambda', {
runtime: lambda.Runtime.PYTHON_3_11,
handler: 'daily.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/daily-alert')),
environment: {
SENDER_EMAIL: senderEmail,
DDB_TABLE_NAME: table.tableName,
},
});

šŸ” Security & IAM

  • IAM Roles for Lambda & Glue
  • VPC endpoints for S3
  • SES verification via CDK

🧩 CDK Snippet:

TypeScript
new ses.EmailIdentity(this, 'EmailIdentity', {
identity: ses.Identity.email(senderEmail),
});

šŸ—ļø Infrastructure with CDK + CI/CD

Everything is defined in CDK and deployed via GitHub Actions.

🧩 GitHub Actions Sample:

YAML
- name: Deploy to AWS
run: |
npm ci
npm run build
npx cdk deploy --require-approval never

šŸ“ˆ Monitoring & Logging

We used CloudWatch for logging and metrics, with Lambda and Glue emitting events for errors or retries.

āœ… Wrapping It Up

With CDK and the AWS ecosystem, we built a fully serverless system together—from data ingestion to user communication. Clone the full code and start building your version of this:

šŸ”— GitHub Repo

Happy coding! ā˜ļø

About the author

Mohamed Abbas writes about AWS, data platforms and AI architecture. His articles connect design decisions with projects, diagrams and implementation details.

About Mohamed Ā· Connect on LinkedIn

Continue reading

Previous article:
Next article:

Leave a comment