BACK_TO_LOGS// STEP_7_OF_10
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.