DEV Community

Cover image for Architecting an AI Crop Disease Scanner API in Laravel 11 ๐Ÿฉบ
Prajapati Paresh
Prajapati Paresh

Posted on Originally published at smarttechdevs.in

Architecting an AI Crop Disease Scanner API in Laravel 11 ๐Ÿฉบ

The Compute-Heavy Conundrum

One of the most powerful features of KhedutBandhu (เช–เซ‡เชกเซ‚เชค เชฌเช‚เชงเซ) is the AI Crop Disease Doctor (AI เชชเชพเช• เชจเชฟเชฆเชพเชจ). A farmer uses our Flutter mobile application to snap a photograph of a decaying leaf. The app sends this image to our backend, which interfaces with a Convolutional Neural Network (CNN) computer vision model. Within seconds, the AI identifies the exact fungal infection or pest attack and returns a step-by-step organic and chemical treatment remedy in the farmer's native language.

From an architectural standpoint, image processing and AI inference are extremely heavy workloads. If the Flutter app makes a synchronous HTTP POST request to upload the 4MB image, and the Laravel controller waits for the Python AI microservice to process the image matrix before returning a response, the request could take 5 to 10 seconds. On a fluctuating rural 3G network, a 10-second synchronous HTTP request will almost certainly result in a timeout. The app crashes, and the farmer loses trust in the platform.

At Smart Tech Devs, we protect our mobile UX by decoupling the upload from the inference. We architected an Asynchronous Event-Driven Image Processing Pipeline using Laravel 11 Job Queues and WebSockets/Polling.

Phase 1: The Fast-Ingest API

The primary goal of the Laravel API is to get the image safely onto the server and terminate the HTTP connection as fast as physically possible. We compress the image, push it to an S3 bucket (or local storage), dispatch a background job, and immediately return a 202 Accepted status with a tracking ID.


namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Jobs\ProcessCropImageInference;
use App\Models\CropScan;

class CropDoctorController
{
    public function uploadScan(Request $request)
    {
        $request->validate(['leaf_image' => 'required|image|max:5120']); // Max 5MB

        // 1. Generate a unique tracking UUID for this scan
        $scanId = Str::uuid();

        // 2. Store the raw image rapidly
        $path = $request->file('leaf_image')->storeAs('crop-scans', "{$scanId}.jpg", 's3');

        // 3. Create a pending record in the database
        CropScan::create([
            'id' => $scanId,
            'user_id' => $request->user()->id,
            'status' => 'processing',
            'image_path' => $path,
        ]);

        // 4. Dispatch the heavy AI inference to a Redis Background Queue
        ProcessCropImageInference::dispatch($scanId);

        // 5. Release the mobile network connection instantly! (Usually under 200ms)
        return response()->json([
            'status' => 'success',
            'message' => 'Image received. AI is analyzing...',
            'scan_id' => $scanId
        ], 202);
    }
}

Phase 2: The Background Worker (AI Inference)

While the farmer's mobile app displays a beautiful, smooth "Analyzing..." animation, our Laravel Queue Worker silently processes the job in the background. It sends the secure S3 URL to our internal AI inference microservice (or an external Computer Vision API) and awaits the classification.


namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Http;
use App\Models\CropScan;
use App\Models\DiseaseKnowledgebase;

class ProcessCropImageInference implements ShouldQueue
{
    use Dispatchable, Queueable;

    public function __construct(public string $scanId) {}

    public function handle()
    {
        $scan = CropScan::find($this->scanId);
        if (!$scan) return;

        try {
            // 1. Call the Python Computer Vision Microservice
            $aiResponse = Http::timeout(10)->post('http://ai-vision.internal/classify', [
                'image_url' => Storage::disk('s3')->url($scan->image_path)
            ]);

            $classification = $aiResponse->json('disease_slug'); // e.g., 'leaf_curl_virus'

            // 2. Fetch the localized remedy from our Admin Knowledgebase
            $remedy = DiseaseKnowledgebase::where('slug', $classification)->first();

            // 3. Update the scan record to completed
            $scan->update([
                'status' => 'completed',
                'disease_detected' => $remedy->name_translations,
                'organic_remedy' => $remedy->organic_treatment_translations,
                'chemical_remedy' => $remedy->chemical_treatment_translations,
            ]);

            // 4. Fire an event to notify the Flutter app (via Pusher/WebSockets)
            event(new \App\Events\CropScanCompleted($scan));

        } catch (\Exception $e) {
            $scan->update(['status' => 'failed']);
            logger()->error("AI Inference failed for scan {$this->scanId}");
        }
    }
}

Phase 3: The Mobile Client Resolution

Because KhedutBandhu serves rural networks where WebSocket connections (like Laravel Reverb or Pusher) can sometimes drop, we architected a resilient fallback mechanism in our Flutter app. If the WebSocket connects, the result appears instantly. If the socket drops, the Flutter app automatically falls back to Short Polling, silently pinging an endpoint (/api/scans/{scan_id}/status) every 3 seconds until the status changes from processing to completed.

The Engineering ROI

By architecting our AI Crop Doctor API asynchronously using Laravel Queues and Webhooks/Polling, we completely insulated our Flutter mobile application from backend computational latency. The farmer experiences a lightning-fast image upload, zero network timeouts, and a highly polished UI that delivers localized agronomical remedies reliably. This decoupled architecture allows us to swap, upgrade, or scale our Computer Vision models in the background without requiring a single update to the frontend mobile application.

Top comments (0)