Expertise technical

Build a resilient SMS waiting line with Redis (Laravel Horizon, Bull, BullMQ)

File waiting resiliente sms redis: technical guide with code examples for developers in Morocco.

Build a resilient SMS waiting line with Redis (Laravel Horizon, Bull, BullMQ)
Table of Contents
  1. If you send your marketing or transaction SMS directly in the same thread as the HTTP response to the client (e.g. the user clicks on "Register", the server makes a synchronous call [SMS API](/fr/api-sms-maroc/) and then returns the web page), your architecture is vulnerable.
  2. Why a simple "for" loop is not enough in production
  3. Laravel Horizon for Mass Delivery (PHP)
  4. Example of Node.js with BullMQ

If you send your marketing or transaction SMS directly in the same thread as the HTTP response to the client (e.g. the user clicks on "Register", the server makes a synchronous call [SMS API](/fr/api-sms-maroc/) and then returns the web page), your architecture is vulnerable.

If the Moroccan SMS Pass (IAM, Orange, inwi) takes 3 seconds to answer due to a network overload, the user will wait 3 seconds in front of a white screen. Worse, if the API is temporarily offline, your script will land, the user will see a 500 error page, and the SMS will be permanently lost.

For robust production, sending SMS must always be asynchronous via a Redis-managed waiting file. Here’s how to implement this resilience in the PHP and Node.js ecosystem.

Why a simple "for" loop is not enough in production

Imagine that you have to route a campaign to 50,000 Moroccan customers. A simple loop foreach or for will saturate your server memory (Memory Exhaustion) and, most importantly, will trigger the Rate Limiting (Throttling) (/fr/blog/throttling-sms-in-morocco-which speed-can-you-really/) mechanism of the API. You will get tens of thousands of '429 Too Many Requests' errors.

The asynchronous architecture solves this problem: 1. Delegation (Producer): Your Web application instantly places 50,000 tasks (Jobs) containing the number and message in a Redis (in memory, ultra-fast) database.The Web page responds to the customer in less than 50ms.2. Execution (Consumer / Worker): A background process “split” Redis requests at its own pace (e.g. 50 requests per minute) to stay under your API key's limit.

Laravel Horizon for Mass Delivery (PHP)

In the PHP/Laravel ecosystem, Horizon (associated with Redis) is the perfect tool. It makes it easy to configure specific waiting rows, throttling and have a visual dashboard.

1. Throttling configuration (App/Jobs/SendSmsJob.php): Instead of flooding the API, we will force the Laravel worker to not exceed 50 requests per minute, with an automatic reset management.

php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\Middleware\RateLimited;

class SendSmsJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $contact;
    public $message;

    // Définir la limite de débit : "sms-api" (défini dans AppServiceProvider)
    public function middleware()
    {
        return [new RateLimited('sms-api')];
    }

    public function handle()
    {
        // Appel API réel vers EnvoiSMS.ma
        $response = EnvoiSms::send($this->contact->phone, $this->message);
        
        if ($response->failed()) {
            throw new \Exception("Erreur API, Horizon relancera le job selon l'exponential backoff.");
        }
    }
}

2. Isolate critical streams: In config/horizon.php, configure two separate "workers" processes. One worker dedicated to the otp_critical row (which should never be slowed down) and another dedicated to the marketing_bulk row (which can quietly turn into a background task).

Example of Node.js with BullMQ

Whether your backend is Express or NestJS, BullMQ (the Bull successor) is the industry standard backed by Redis for managing millions of jobs.

Native Rate Limiting configuration in BullMQ:

javascript
import { Queue, Worker } from 'bullmq';

// Création de la file d'attente liée à Redis
const smsQueue = new Queue('sms_dispatch', {
  connection: { host: 'localhost', port: 6379 }
});

// Ajout d'un SMS dans la file (Hyper rapide, le client n'attend pas)
await smsQueue.add('send_otp', { phone: '+212600000000', message: 'Code: 1234' });

// Le Worker qui traite l'envoi en respectant les limites de l'opérateur
const smsWorker = new Worker('sms_dispatch', async job => {
  const { phone, message } = job.data;
  
  try {
    await sendSmsViaApi(phone, message); // Votre appel HTTP axios ou fetch
  } catch (error) {
    // Si l'erreur est temporaire (ex: 503), BullMQ relancera le job
    throw new Error('Erreur API, réessayer plus tard.');
  }
}, {
  connection: { host: 'localhost', port: 6379 },
  limiter: {
    max: 50, // Maximum 50 requêtes...
    duration: 60000 // ...par minute, sous la limite de votre clé API (60 à 1 200 req/min selon l'offre).
  }
});

By using these tools, you not only guarantee that the API load is perfectly smooth, but most importantly, if your server crashes in the middle of sending the campaign, the unsended SMS remains stored in Redis.

Why choose EnvoiSMS for your business?

Carrier Delivery

Local routes to IAM, Orange and Inwi, with automatic failover between routes and delivery status sent by webhook.

Cost Optimization

WhatsApp Business API from 0.65 MAD per message with optimal ROI.

Sovereign Data (CNDP)

Full compliance with Moroccan personal data protection regulations (CNDP).

How do I track routing on the IAM, Inwi and Orange networks?
Our local routes to Maroc Telecom (IAM), Orange and inwi, with automatic failover between routes, return a carrier delivery status for every message, by webhook. Delivery time depends on the recipient's carrier and network.
How does the platform manage the format of Moroccan numbers?
EnvoiSMS automatically normalizes the numbers entered (06..., 07..., 00212...) to the E.164 standard (+212) to avoid any delivery failure.

Suggested Articles

SMS Formatting in Morocco: Line Breaks, GSM-7 vs UCS-2 Encoding and Cost Optimization
technical

SMS Formatting in Morocco: Line Breaks, GSM-7 vs UCS-2 Encoding and Cost Optimization

WhatsApp Business Formatting in Morocco: Enriched Text, Emojis and Interactive Buttons
technical

WhatsApp Business Formatting in Morocco: Enriched Text, Emojis and Interactive Buttons

One-Tap Autofill & 1-Click WhatsApp OTP Authentication in Morocco
technical

One-Tap Autofill & 1-Click WhatsApp OTP Authentication in Morocco