BACK_TO_LOGS// STEP_7_OF_10
HANDBOOK_GUIDE2026-06-29

OptiCrop: Smart Agricultural Production Optimization Engine ML 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().
  • JSON Exchange: JavaScript on the frontend will capture user entries and package them as JSON. The backend extracts this JSON (request.get_json()), converts the values to floats/integers, and packs them into a NumPy array matching the exact structure used during model training.
  • Making Predictions: The Flask server loads our serialized model weights (joblib.load('models/crop_model.joblib')) in memory. When it receives a request, it calls model.predict() and returns the recommended crop label back to the client.
  • 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, jsonify, render_template
import joblib
import numpy as np
import os

app = Flask(__name__)

# Load the saved model and scaler
MODEL_PATH = 'models/crop_model.joblib'
SCALER_PATH = 'models/scaler.joblib'

if not os.path.exists(MODEL_PATH) or not os.path.exists(SCALER_PATH):
    print("ERROR: Model or Scaler not found! Please run train.py first.")
    exit(1)

model = joblib.load(MODEL_PATH)
scaler = joblib.load(SCALER_PATH)

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/predict', methods=['POST'])
def predict():
    try:
        data = request.get_json()
        
        # Build features list in the exact order the model expects:
        # N, P, K, temperature, humidity, ph, rainfall
        features_raw = np.array([[
            float(data['N']),
            float(data['P']),
            float(data['K']),
            float(data['temperature']),
            float(data['humidity']),
            float(data['ph']),
            float(data['rainfall'])
        ]])
        
        # Apply boundary checks
        n, p, k, temp, hum, ph, rain = features_raw[0]
        if not (0 <= n <= 250): return jsonify({'error': 'Nitrogen must be between 0 and 250 mg/kg.'}), 400
        if not (0 <= p <= 250): return jsonify({'error': 'Phosphorus must be between 0 and 250 mg/kg.'}), 400
        if not (0 <= k <= 300): return jsonify({'error': 'Potassium must be between 0 and 300 mg/kg.'}), 400
        if not (-10 <= temp <= 60): return jsonify({'error': 'Temperature must be between -10°C and 60°C.'}), 400
        if not (0 <= hum <= 100): return jsonify({'error': 'Humidity must be between 0% and 100%.'}), 400
        if not (0 <= ph <= 14): return jsonify({'error': 'Soil pH must be between 0.0 and 14.0.'}), 400
        if not (0 <= rain <= 1000): return jsonify({'error': 'Rainfall must be between 0mm and 1000mm.'}), 400

        # Apply the StandardScaler fitted during training
        features_scaled = scaler.transform(features_raw)
        
        # Predict optimal crop
        prediction = model.predict(features_scaled)[0]
        
        return jsonify({
            'success': True,
            'crop': str(prediction).capitalize()
        })
    except Exception as e:
        return jsonify({'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 soil 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, style.css, and script.js) that will talk to this Flask backend.