HANDBOOK_GUIDE2026-06-29
Human Development Index Predictor: End-to-End Machine Learning Guide
By Rayan Syed
45 min read
Part 7: Flask API Backend
Save this script as app.py in your project folder.
7.1 Understanding Backend Web Servers & API Routing
In this phase, we build a local web backend using Flask.
- API Routing: Routing is the mechanism that maps network URLs (endpoints) to specific Python functions. In Flask, we define routes using
@app.route(). - Request Handling: We set up endpoints to render HTML templates. When a client submits the index form using the HTTP POST method, the backend parses input fields, log-transforms the submitted GNI value, runs predictions, and returns a template populated with the score value.
- Why We Need a Server: You might wonder: why can’t we run predictions directly in browser JavaScript? In real-world applications, machine learning models can weigh several hundred megabytes. Loading these models directly into the user’s web browser would freeze their system or crash the page. Running the model on a dedicated backend server keeps the client lightweight and secure.
from flask import Flask, request, render_template
import joblib
import numpy as np
import os
app = Flask(__name__)
# Load the saved model
MODEL_PATH = 'models/hdi_model.joblib'
if not os.path.exists(MODEL_PATH):
print("ERROR: Model not found! Please run train.py first.")
exit(1)
model = joblib.load(MODEL_PATH)
@app.route('/')
def home():
return render_template('index.html')
@app.route('/predict', methods=['POST'])
def predict():
try:
# Extract form field values submitted by index.html form action
life = float(request.form['life_expectancy'])
expec_school = float(request.form['expec_yr_school'])
mean_school = float(request.form['mean_yr_school'])
gni = float(request.form['gross_inc_percap'])
# Simple parameter boundaries validation
if not (30 <= life <= 110): return "Error: Life expectancy must be between 30 and 110 years.", 400
if not (0 <= expec_school <= 30): return "Error: Expected years of schooling must be between 0 and 30.", 400
if not (0 <= mean_school <= 30): return "Error: Mean years of schooling must be between 0 and 30.", 400
if not (100 <= gni <= 250000): return "Error: GNI per capita must be between $100 and $250,000.", 400
# Apply logarithmic transform to GNI to align with model scale
log_gni = np.log(gni)
# Package into raw feature array matching model columns
features = np.array([[life, expec_school, mean_school, log_gni]])
# Predict continuous HDI target
prediction = model.predict(features)[0]
# Ensure result bounds are realistic (clamped between 0.000 and 1.000)
final_score = float(np.clip(prediction, 0.0, 1.0))
return render_template('result.html', score=round(final_score, 3))
except Exception as e:
return f"Server Error: {str(e)}", 400
if __name__ == '__main__':
app.run(port=5000, debug=True)
Checkpoint: Backend Complete
At this stage, your local Flask backend is complete and ready to receive HDI applications. However, if you visit the server now, you will see a TemplateNotFound error. This is because we haven’t created the frontend form files yet! In the next part, we will build the user interface files (index.html, result.html, and style.css) that will talk to this Flask backend.