An AI-powered weather app that delivers funny, location-aware forecasts with generated images. Enter a city and get a real weather report written by AI — with a unique illustration to match the conditions.
A request for a forecast kicks off a Step Functions pipeline:
- Provider fan-out — three weather providers (OpenWeatherMap, WeatherAPI, Open-Meteo) are fetched in parallel
- Agent 1: Compare — Bedrock (Claude Haiku) normalizes and reconciles the provider data into a consensus weather summary
- Agent 2: Funny text — Bedrock writes a humorous, localized forecast narrative in the user's language
- Agent 3: Image generation — Pixazo AI generates an illustration matching the conditions; cached by location + conditions to avoid redundant calls
- Save & serve — result is stored in DynamoDB + S3 and streamed back to the browser via WebSocket
Cache hits at either the weather or image level short-circuit the pipeline. Real-time progress is pushed to the browser as each stage completes.
| Concern | Technology |
|---|---|
| Language | TypeScript (end-to-end) |
| Runtime | Node.js 20+ |
| IaC | SST v3 (Ion) → CloudFormation / Pulumi |
| Cloud | AWS (serverless-only) |
| Frontend | Vite + React SPA |
| AI — text | Amazon Bedrock (Claude Haiku 4.5) |
| AI — images | Pixazo AI SDXL v1.0 REST API |
| Database | DynamoDB (multi-table, on-demand) |
| Storage | S3 + CloudFront CDN |
| Orchestration | AWS Step Functions Standard Workflow |
| Real-time | API Gateway WebSocket + EventBridge |
| Observability | CloudWatch Logs, Metrics, Dashboards, Alarms, X-Ray |
packages/
core/ # Shared types, prompts, utilities
functions/ # All Lambda handlers (providers, agents, API, WebSocket)
web/ # Vite + React SPA
infra/ # SST resource definitions (API, storage, pipeline, monitoring)
docs/ # Architecture decision records, technical spec, implementation plan
Prerequisites: Node.js 20+, pnpm, an AWS account with a profile named uweather, and API keys for OpenWeatherMap, WeatherAPI, and Pixazo.
# Install dependencies
pnpm install
# Set secrets (one-time)
pnpm sst secret set OpenWeatherApiKey <key>
pnpm sst secret set WeatherApiKey <key>
pnpm sst secret set PixazoApiKey <key>
# Start SST dev mode (live Lambda, hot reload)
pnpm dev
# Local web dev server (in a separate terminal)
pnpm --filter web devpnpm dev # SST dev mode with live Lambda
pnpm build # TypeScript build (all packages)
pnpm typecheck # Type-check without emitting
pnpm lint # Biome lint
pnpm check # Biome lint + format (writes)
pnpm deploy-dev # Deploy to dev stage
pnpm deploy-prod # Deploy to prod stage (requires WEB_ORIGIN)
pnpm deploy-prod-first # First prod deploy (CORS temporarily open)dev— development, used withsst devfor live Lambda debugging. Deployed automatically on push tomainvia GitHub Actions.prod— production. Deployed manually via GitHub Actionsworkflow_dispatchtrigger.
Production requires a two-pass deploy because the CORS origin (CloudFront URL) isn't known until the first deployment creates the distribution.
Prerequisites (one-time):
- Ensure Bedrock model access is approved for Claude Haiku 4.5 in your AWS region.
- Set secrets for the prod stage:
pnpm sst secret set OpenWeatherApiKey <key> --stage prod
pnpm sst secret set WeatherApiKey <key> --stage prod
pnpm sst secret set PixazoApiKey <key> --stage prodFirst deploy — provisions all resources, CORS temporarily allows all origins:
pnpm deploy-prod-first
# or via GitHub Actions: trigger "Deploy Prod" workflow with "first_deploy" checkedSubsequent deploys — lock down CORS to the CloudFront web URL from the first deploy:
WEB_ORIGIN=https://dXXXX.cloudfront.net pnpm deploy-prod
# or via GitHub Actions: trigger "Deploy Prod" workflow with web_origin filled inPost-deploy verification:
curl https://<api-url>/health— should return 200.- Trigger a forecast from the web app and verify the full pipeline runs.
- Confirm the SNS alarm subscription email (check inbox for AWS notification).
- Open the
uweather-prodCloudWatch dashboard and verify metrics are populating.
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /forecast?city=... |
Start or return a cached forecast |
| GET | /forecast/status?executionArn=... |
Poll pipeline execution status |
| GET | /history |
Past forecasts for the current session |
Real-time progress updates are delivered over a WebSocket connection. The browser opens a connection before triggering a forecast; Step Functions emits stage events via EventBridge, which fan out to all active connections for that execution.
All resources are defined under infra/ and composed in sst.config.ts:
storage.ts— DynamoDB tables (WeatherCache, Forecasts, Users, WebSocketConnections), S3 bucket, CloudFront distributionspipeline.ts— Step Functions state machine (ForecastPipeline), all pipeline Lambda functionsapi.ts— API Gateway HTTP API and Lambda handlersrealtime.ts— API Gateway WebSocket API, EventBridge rules, push-stage Lambdamonitoring.ts— CloudWatch dashboards, alarms, SNS topic
Custom CloudWatch EMF metrics are emitted by each Lambda:
WeatherCacheHit/WeatherCacheMissImageCacheHit/ImageCacheMissProviderSuccess/ProviderError(per provider)BedrockLatency,BedrockThrottledImageGenerationCount,LowConfidenceForecast
A CloudWatch dashboard (uweather-{stage}) aggregates these across 7 panels. Four alarms cover Step Functions failures, provider error rate, orchestrator P99 latency, and Bedrock throttle rate.
All Lambdas and the Step Functions state machine have X-Ray active tracing enabled, with traces propagating end-to-end from API Gateway through to each provider and agent Lambda.
- One Lambda per concern — providers, agents, API handlers, and WebSocket handlers are all separate functions
- All AI prompts live in
packages/core/src/prompts/as typed template functions - Shared utilities (
degreesToCardinal,stripMarkdownFence,getTempBucket,buildImageCacheKey, etc.) live inpackages/core/src/utils/— check there before writing a new one - Biome handles linting and formatting; a pre-push hook enforces it
- See
docs/adr/001-architecture.mdfor the reasoning behind major infrastructure choices - See
docs/TECHNICAL_SPEC.mdfor DynamoDB schemas and access patterns
MIT