Skip to content

nitinmohan18/windly-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

6 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌬️ Windly β€” Backend

The atmospheric intelligence engine powering Windly's cinematic weather experience.

Live API: windly-backend.onrender.com Β |Β  Frontend Repo: windly-frontend Β |Β  Docs: /docs Β |Β  FastAPI Python Render


🧠 About the Backend

This is not a simple weather proxy. The Windly backend is a purpose-built atmospheric intelligence service responsible for four distinct concerns:

  • Weather Aggregation β€” Proxies WeatherAPI's forecast and search endpoints, normalises errors, and returns clean structured JSON to the frontend
  • AI Prediction Engine β€” Runs a calibrated Random Forest classifier to estimate rain probability from 5 atmospheric inputs, served via a dedicated /predict endpoint
  • TTL Cache Layer β€” In-memory caching with per-endpoint TTLs prevents redundant API calls on repeated searches and rapid unit-toggle interactions
  • Async API Orchestration β€” A single shared httpx.AsyncClient persists across the server's lifetime, reusing TCP connections and keep-alive sessions for every outbound request

The result is a backend that feels fast, handles failure gracefully, and keeps the WeatherAPI key off the client entirely.


πŸ—οΈ Architecture

Browser / Vercel Frontend
          β”‚
          β”‚ HTTPS
          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         FastAPI Backend (Render)        β”‚
β”‚                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚       GZip Middleware           β”‚    β”‚
β”‚  β”‚  (compresses responses β‰₯500B)   β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                 β”‚                       β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚         β”‚                β”‚              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚ WeatherAPI  β”‚  β”‚  ML Inferenceβ”‚      β”‚
β”‚  β”‚   Proxy     β”‚  β”‚   Engine     β”‚      β”‚
β”‚  β”‚ /forecast   β”‚  β”‚  /predict    β”‚      β”‚
β”‚  β”‚ /search     β”‚  β”‚              β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚         β”‚                β”‚              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚  TTL Cache  β”‚  β”‚ RandomForest β”‚      β”‚
β”‚  β”‚  (in-memory)β”‚  β”‚  .pkl model  β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚         β”‚                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚  β”‚  Shared Async httpx.Client    β”‚      β”‚
β”‚  β”‚  (connection pool, keep-alive)β”‚      β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚
          β–Ό
    WeatherAPI.com

πŸ”Œ API Reference

Endpoints

Method Endpoint Cache TTL Description
GET / β€” Health check
POST /predict β€” Rain probability prediction
GET /weather/forecast?q={city} 5 min 3-day weather forecast
GET /weather/search?q={query} 15 min City autocomplete suggestions

POST /predict

Runs the Random Forest classifier and returns a calibrated rain probability.

Request body:

{
  "temperature": 22.5,
  "humidity": 78,
  "pressure": 1008,
  "wind": 35,
  "cloud": 60
}

Response:

{
  "prediction": 1,
  "probability": 0.7341
}

Field validation (enforced by Pydantic):

Field Type Range
temperature float βˆ’90 to 60 Β°C
humidity float 0 to 100 %
pressure float 870 to 1085 mb
wind float 0 to 300 km/h
cloud float 0 to 100 %

cURL example:

curl -X POST https://windly-backend.onrender.com/predict \
  -H "Content-Type: application/json" \
  -d '{
    "temperature": 22.5,
    "humidity": 78,
    "pressure": 1008,
    "wind": 35,
    "cloud": 60
  }'

GET /weather/forecast

Proxies WeatherAPI's 3-day forecast endpoint. Returns current conditions, hourly data, and forecast days including AQI.

curl "https://windly-backend.onrender.com/weather/forecast?q=London"

GET /weather/search

Returns city name suggestions for autocomplete. Results are cached for 15 minutes β€” this endpoint fires on every keystroke after 3 characters, so caching meaningfully reduces WeatherAPI usage.

curl "https://windly-backend.onrender.com/weather/search?q=mum"

πŸ“˜ Interactive API Docs

FastAPI generates full interactive documentation automatically from the code.

Interface URL Description
Swagger UI /docs Try every endpoint live in the browser
ReDoc /redoc Clean reference documentation

No API key needed to explore the docs β€” the WeatherAPI key is server-side only.


πŸ§ͺ AI Model & Training Pipeline

Model Overview

A Random Forest classifier with sigmoid probability calibration, trained on the Global Weather Repository dataset. Raw forest probabilities tend to be overconfident; sigmoid calibration via CalibratedClassifierCV produces realistic, well-distributed outputs across the full 0–1 range.

Model configuration:

RandomForestClassifier(
    n_estimators=120,
    max_depth=10,
    min_samples_split=4,
    n_jobs=-1,
    random_state=42,
)
# Wrapped with:
CalibratedClassifierCV(rf, method='sigmoid', cv=5)

Training Pipeline

GlobalWeatherRepository.csv
        β”‚
        β–Ό
1. Load CSV β€” select 5 feature columns + precip_mm
        β”‚
        β–Ό
2. Drop NaN rows β€” ensures clean training signal
        β”‚
        β–Ό
3. Generate binary target β€” precip_mm > 0 β†’ RainTomorrow = 1
        β”‚
        β–Ό
4. Stratified 80/20 train/test split (random_state=42)
        β”‚
        β–Ό
5. Fit RandomForest + sigmoid calibration (5-fold CV)
        β”‚
        β–Ό
6. Evaluate β€” Accuracy, ROC-AUC, classification report
        β”‚
        β–Ό
7. Export β†’ model/random_forest.pkl (joblib)

Run training locally

# Place GlobalWeatherRepository.csv in data/
python model/train.py

# Output: model/random_forest.pkl
# Logs:   accuracy, ROC-AUC, rain rate, classification report

⚑ Performance Optimizations

Shared Async HTTP Client

A single httpx.AsyncClient is created at server startup via FastAPI's lifespan context manager and stored on app.state. Every weather request reuses this client instead of opening a new TCP connection β€” saving 30–80ms per request.

app.state.http = httpx.AsyncClient(
    timeout=8.0,
    limits=httpx.Limits(
        max_connections=20,
        max_keepalive_connections=10,
        keepalive_expiry=30.0,
    ),
)

TTL Cache

An in-memory dict cache avoids redundant WeatherAPI calls. Weather data changes slowly enough that caching for a few minutes is invisible to users but saves meaningful API quota.

Endpoint TTL Reason
/weather/forecast 5 minutes Forecast data doesn't change second-to-second
/weather/search 15 minutes City names are stable; fires on every keystroke

GZip Compression

GZipMiddleware compresses all responses over 500 bytes. A typical forecast JSON response is 10–20 KB β€” compression cuts this to 2–4 KB, meaningfully improving load time on mobile connections.

Persistent Model Loading

The random_forest.pkl model is loaded once at module import time in predict.py. It is never re-loaded between requests. This eliminates model deserialisation overhead (which can take 200–400ms) from every prediction call.

Async Request Handling

All WeatherAPI proxy routes are async def, allowing the server to handle other requests while waiting on the external API. Under concurrent load, this prevents request queuing that would occur with synchronous handlers.


πŸ’‘ Why FastAPI?

Feature Benefit
Async-native async def routes handle I/O-bound tasks (external API calls) without blocking the server
Pydantic validation Request body validation and type coercion with zero boilerplate β€” invalid inputs are rejected automatically with clear error messages
Auto-generated docs /docs and /redoc are generated from the code itself β€” always up to date, no manual maintenance
Lightweight No ORM, no admin panel, no unnecessary abstractions β€” just routes, middleware, and models
Production-ready Uvicorn + FastAPI is a mature, battle-tested stack used in production at scale

πŸ—‚οΈ File Structure

backend/
β”œβ”€β”€ app.py                      # FastAPI app β€” routes, middleware, lifespan, caching
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ .env                        # WEATHER_API_KEY β€” never committed
β”œβ”€β”€ model/
β”‚   β”œβ”€β”€ train.py                # Full training pipeline β€” loads, cleans, trains, exports
β”‚   β”œβ”€β”€ predict.py              # Model loader + inference function
β”‚   β”œβ”€β”€ random_forest.pkl       # Trained model artifact β€” must be committed for deployment
β”‚   └── feature_columns.json   # Feature name reference
└── data/
    └── GlobalWeatherRepository.csv   # Training dataset β€” gitignored, download from Kaggle

πŸš€ Running Locally

1. Clone & install

git clone https://github.com/nitinmohan18/windly-backend.git
cd windly-backend
python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt

2. Configure environment

echo "WEATHER_API_KEY=your_key_here" > .env

Get a free key at weatherapi.com. The free tier covers all of Windly's usage.

3. Train the model

Download GlobalWeatherRepository.csv from Kaggle and place it in data/.

python model/train.py
# Outputs: model/random_forest.pkl
# Logs accuracy, ROC-AUC, and classification report to the console

4. Start the server

uvicorn app:app --reload --port 8000
  • API: http://localhost:8000
  • Interactive docs: http://localhost:8000/docs

🌐 Deployment (Render)

Setup

  1. Push this repo to GitHub (including model/random_forest.pkl β€” see note below)
  2. Create a new Web Service on Render
  3. Configure:
Setting Value
Runtime Python 3
Build Command pip install -r requirements.txt
Start Command uvicorn app:app --host 0.0.0.0 --port $PORT
  1. Add environment variable in the Render dashboard:
Key Value
WEATHER_API_KEY your WeatherAPI key

Important Notes

random_forest.pkl must be committed to the repo. predict.py loads the model at import time β€” if the file doesn't exist when the server starts, the /predict endpoint will be unavailable. Render has no way to run train.py at deploy time (no training dataset on the server), so the trained artifact must be version-controlled.

Cold starts on the free tier. Render's free tier spins down services after ~10 minutes of inactivity. The first request after sleep can take 30–40 seconds while the server wakes up and loads the model. Windly's frontend handles this with a 50-second timeout and a user-facing waiting message β€” no action needed on the backend side.

.env and data/ must be gitignored. Your WeatherAPI key and the training CSV should never be committed. Confirm both are in .gitignore before pushing.


πŸ› οΈ Tech Stack

Layer Technology
Framework FastAPI
Server Uvicorn
HTTP Client httpx (async, connection-pooled, keep-alive)
Compression Starlette GZipMiddleware
Validation Pydantic v2
ML β€” Classifier scikit-learn RandomForestClassifier
ML β€” Calibration scikit-learn CalibratedClassifierCV (sigmoid)
ML β€” Serialisation joblib
Environment python-dotenv
Hosting Render

πŸ“„ License

MIT β€” fork it, extend it, build on it.

About

windly weather app backend code

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages