NimbusCast.
A weather app that pairs live forecasts for any city with a poem matched to its sky. React front end, Express API on Vercel serverless.
- ROLE
- Full-Stack Engineer: design, frontend, API, deployment
- TEAM
- Solo
- DATE
- Oct 2023 to Nov 2023
- TRACK
- Full-Stack
- STACK
- React, TypeScript, Express, Vercel, Tailwind CSS

FIG. 03B ARCHITECTURE
- React SPA (Vite), frontend. Connects to Express on Vercel Functions (/weather · /poem). See note 3.
- City search, frontend. Connects to Express on Vercel Functions (/cities/suggestions). See note 5.
- Express on Vercel Functions, backend. Connects to Response cache (GEO · WEATHER · FORECAST), Poem ranker + fallback (KEYWORD). See note 1.
- Response cache, storage. Connects to OpenWeatherMap (ON MISS). See note 2.
- Poem ranker + fallback, backend. Connects to PoetryDB (5S TIMEOUT). See note 4.
- OpenWeatherMap, external.
- PoetryDB, external.
NOTES
Decision
I wrote one Express app and exported it as a Vercel serverless function, with a small dev.ts that runs the same app on a port locally. Production and local development share the same routes, and every /api route goes through a per-IP rate limiter first.
Trade-off
I let Vercel reuse a weather answer for 5 minutes, since the data barely changes in that time. Repeat searches come back about 2.5 times faster and don't use my free API quota. The trade-off is that the weather can be up to 5 minutes old.
What I got wrong
At first the whole page waited on a single loading flag, so the weather stayed hidden until the third-party poem API answered. Now the weather renders right away, the poem loads on its own, and a failed refetch keeps the last good data on screen.
Failing gracefully
The ranker asks PoetryDB for 5 random poems and picks the one that uses the weather word most often, so 'clouds' also matches 'cloud'. If PoetryDB fails or has no match, it serves one of 10 local poems instead of an error.
Search that doesn't race
Suggestions wait 300 ms after you stop typing. Each new keystroke cancels the previous request, so an old, slow response can't overwrite a newer one.
RESULTS
- REPEAT SEARCH, FROM VERCEL'S EDGE CACHE
- 0.1s
- MEDIAN FIRST SEARCH (3 UPSTREAM CALLS)
- 0.27s
- UPSTREAM CALLS ON A REPEAT WITHIN 5 MIN
- 0
PROJECT 08 / 08