AWS Builder Center
The Forecast Said “Hot.” HeatShift Asked “When?”

The Forecast Said “Hot.” HeatShift Asked “When?”

HeatShift turns an hourly weather forecast into a practical scheduling choice. Here’s how I built the planner with React, AWS Lambda, API Gateway, and Amplify—and what the edge cases taught me.

At 1 p.m., a heat warning can tell you that it is hot. It cannot tell you whether moving a two-hour outdoor task to 10 a.m. would make the forecast meaningfully cooler.
That is the small, practical question behind HeatShift: if a task can happen at different times, which available window has the lowest forecast “feels-like” temperature?
I built HeatShift as a solo project for the Heat and Water track at Environmental Hacks, part of the Bharat Builds Tour by WeMakeDevs with AWS. It is aimed at people who spend time outdoors—delivery riders, municipal and construction workers, commuters, students, and sports teams—when they have some flexibility in their schedule. It is a planning aid, not medical or safety guidance.

From a forecast to a choice

Most weather pages show the forecast hour by hour. HeatShift takes the next step: the user chooses a location, activity, task duration, planned start, and available time range. The app evaluates every feasible contiguous start window of that duration, averages the hourly apparent-temperature values in each window, and recommends the lowest-average option. It also compares that suggestion with the user’s original plan.
For example, if someone has two hours available sometime between 10 a.m. and 5 p.m., HeatShift compares each two-hour candidate—not just the coolest individual hour. That distinction matters: a task needs a continuous block, and one pleasant hour does not make a good two-hour plan.
The result includes the planned and suggested blocks, their average forecast feels-like temperatures, the difference, ranked alternatives, and an interactive hourly chart. Users can also prioritize avoiding rain when the forecast has enough useful variation. The frontend supports location search, device location, Celsius/Fahrenheit, dark mode, and sharing a plan on demand.

Why AWS is part of the product

The recommendation path is deliberately small and serverless:
plaintext
React + Vite app (AWS Amplify Hosting)
│ GET /recommend
▼
Amazon API Gateway HTTP API
▼
AWS Lambda (Node.js) ──> Open-Meteo Forecast API
│
└── returns the trimmed forecast and recommendation
The React application is hosted on AWS Amplify Hosting. Amazon API Gateway exposes the recommendation endpoint, and the AWS Lambda function validates the request, fetches forecast data from Open-Meteo, and computes the best block. The function is Node.js ESM with no runtime dependencies. The Lambda deployment archive is staged in Amazon S3; AWS IAM gives the function its minimal execution permissions; and Amazon CloudWatch Logs provides debugging logs with seven-day retention. The API is CORS-restricted to the app’s origin and throttled.
Not every weather request goes through Lambda: the recommendation forecast is fetched server-side, while the separate current-conditions overview and place lookup use Open-Meteo from the browser. Reverse place names use OpenStreetMap Nominatim. The app has no database, accounts, or cookies; the plan is not persisted server-side.

What fought back

ES modules and Lambda packaging. My first deployment taught me that the zip needs the backend’s  package.json  with  "type": "module" . Without it, Lambda reported  Cannot use import statement outside a module . The fix was simple; the more useful lesson was to treat the deployment archive as part of the application and verify its contents before uploading.
Local time is not a string-formatting detail. The planner uses the forecast location’s local hours. An end-of-day selection of hour 24 must map to midnight on the next date ( T00:00 ), not emit  T24:00  and hope the runtime parses it. I made the mapping explicit and tested the boundary.
Two ranking paths need one answer. The backend returns the recommendation, while the frontend ranks the returned hourly series to let users compare alternatives. I added parity checks so the frontend’s top option agrees with the backend’s suggested block. Rain-priority mode also has to fall back transparently when the forecast does not contain enough rain variation to rank meaningfully.
Deployment details can hide in archive layout. Amplify’s custom security headers are supplied by  customHttp.yml  at the root of the deployed  dist  contents. Zipping the wrong directory level can omit that configuration. I verified the deployed headers after publishing instead of assuming a successful job meant the intended configuration was live.
A share link should be intentional. A plan can include location coordinates. I changed the app so submitting a plan does not put those values in the browser address bar; a share URL is created only when the user explicitly chooses “Copy link.”
These edge cases shaped the test suite as much as the normal flow. The backend has 20 tests; the frontend has 38. I also ran lint/build checks and an axe-core scan across six rendered states.

What the estimate means—and does not mean

HeatShift compares forecast values, not on-site measurements. Forecasts cannot account for every factor that changes a person’s exposure, including shade, workload, clothing, hydration, or local microclimates. A cooler forecast window does not certify that work is safe. People should follow official heat guidance and workplace safety procedures.
For an individual plan, HeatShift shows the task duration and the forecast-temperature difference between the planned and suggested windows. Estimating aggregate task-hours shifted would require user-reported or consented usage data; HeatShift does not collect or track that data. The app does not claim to prevent illness or measure a real-world health outcome.
Weather data is provided by  Open-Meteo (CC BY 4.0) , based on multiple national weather services. Reverse-geocoded place names are provided by  OpenStreetMap contributors .

Try it and explore the code

I built the project with React, Vite, and Node.js, using opencode for AI-assisted coding and the AWS Agent Toolkit (MCP server and skills) for AWS development tasks. I did not use Claude or Cursor.
The goal was not to build another heat dashboard. It was to make one scheduling decision easier to see: within the hours I have, which continuous block looks cooler in the forecast?
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article