Agri-weather dashboard for East African farms β weather, AI insights & tree-canopy analysis via a key-isolating backend proxy. Vite/React + Express/TypeScript, with caching, rate-limit awareness, and resilient fallbacks. Deployed on Render.
# πΏ Agri-Weather Intelligence Dashboard
A weather, AI-insight, and tree-canopy-analysis dashboard for East-African farms,
built on the WeatherAI API.
It consumes all three meaningful API families β **weather/forecast**, **AI insights**
(with graceful degradation + Swahili), and **agroforestry tree analysis** β through a
backend proxy that keeps the API key off the browser entirely.
> **π Live demo:** **
agri-weather-intelligence-dβ¦
> **π API proxy:**
agri-weather-server.onrendeβ¦
>
> β³ _Hosted on Render's free tier, so the backend **sleeps after ~15 min of
> inactivity** β the first request may take ~50s to cold-start, then it's fast.
> If a panel errors on first load, hit **Retry** once._
---
## Screenshots
## Architecture
```
ββββββββββββββββββββββββββββ ββββββββββββββββββββββββββ
Browser βββββββΆ β client (Vite + React) β β server (Express + TS) β
(no key) β Render Static Site β /api/* β Render Web Service β
β VITE_API_BASE_URL ββββββββΌβββββββββΆβ injects Bearer wai_β¦ β
ββββββββββββββββββββββββββββ β cache Β· rate tracking β
βββββββββββββ¬βββββββββββββ
β Authorization: Bearer
βΌ
api.weather-ai.co
```
The browser **never** holds the API key. It calls our proxy (`/api/...`); the Express
server injects `Authorization: Bearer wai_β¦` from a server-side env var and returns the
upstream response. The key never appears in the JS bundle or the network tab. This split β
a static frontend and a thin proxy backend β is the one architectural decision everything
else hangs off.
## Features
- **Current conditions + 7-day forecast** with a **daily/hourly toggle** (hourly is fetched
lazily, only when opened).
- **Opt-in AI insight** (`/v1/insights`): **off by default** to protect the scarce 200/mo AI
budget β the user clicks "Generate AI insight" to spend one. On the free plan the endpoint
is Pro/Scale-gated and returns `403`, which the card renders as a clean notic β¦