Laravel Job Batching, Chaining &amp; Rate Limiting | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues        On this page       1. [  Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues ](#job-batching-chaining-and-rate-limited-middleware-in-laravel-queues)
2. [  Batches: Parallel Work With a Shared Lifecycle ](#batches-parallel-work-with-a-shared-lifecycle)
3. [  Chains: Sequential Pipelines With Shared Context ](#chains-sequential-pipelines-with-shared-context)
4. [  Rate-Limited Job Middleware ](#rate-limited-job-middleware)
5. [  Takeaways ](#takeaways)

  ![Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues](https://cdn.msaied.com/167/07cf94b1aa8fcead662b9cd5af47acb6.png)

  #laravel   #queues   #jobs   #async  

 Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues 
=======================================================================

     14 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues  ](#job-batching-chaining-and-rate-limited-middleware-in-laravel-queues)
2. [  02   Batches: Parallel Work With a Shared Lifecycle  ](#batches-parallel-work-with-a-shared-lifecycle)
3. [  03   Chains: Sequential Pipelines With Shared Context  ](#chains-sequential-pipelines-with-shared-context)
4. [  04   Rate-Limited Job Middleware  ](#rate-limited-job-middleware)
5. [  05   Takeaways  ](#takeaways)

 Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues
---------------------------------------------------------------------

Laravel's queue system is deceptively deep. Most teams dispatch individual jobs and call it done. But production workflows demand composition: run a set of jobs in parallel, then trigger a follow-up only when all succeed, and never hammer a third-party API faster than its rate limit allows. Laravel gives you all three primitives — batches, chains, and job middleware — and combining them correctly is where the real power lives.

---

### Batches: Parallel Work With a Shared Lifecycle

A batch dispatches multiple jobs concurrently and exposes callbacks for completion, failure, and progress.

```php
use Illuminate\Support\Facades\Bus;

$batch = Bus::batch([
    new ProcessInvoice($invoice1),
    new ProcessInvoice($invoice2),
    new ProcessInvoice($invoice3),
])
->then(fn (Batch $batch) => SendBatchSummary::dispatch($batch->id))
->catch(fn (Batch $batch, Throwable $e) => Log::error('Batch failed', [
    'batch' => $batch->id,
    'error' => $e->getMessage(),
]))
->finally(fn (Batch $batch) => Cache::forget('invoices:processing'))
->name('Invoice Processing')
->allowFailures() // don't cancel remaining jobs on a single failure
->dispatch();

```

Store `$batch->id` if you need to poll progress from a UI. `$batch->progress()` returns 0–100 based on processed job count.

**Important:** Every job in a batch must use the `Batchable` trait, and you should check `$this->batch()->cancelled()` at the start of `handle()` to respect early cancellation.

```php
use Illuminate\Bus\Batchable;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessInvoice implements ShouldQueue
{
    use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        if ($this->batch()?->cancelled()) {
            return;
        }

        // ... process
    }
}

```

---

### Chains: Sequential Pipelines With Shared Context

Chains execute jobs one after another; if any job fails, the rest are abandoned.

```php
Bus::chain([
    new ValidateOrder($order),
    new ChargePayment($order),
    new FulfillOrder($order),
    new SendConfirmationEmail($order),
])
->catch(fn (Throwable $e) => $order->markFailed($e->getMessage()))
->dispatch();

```

You can mix batches inside chains for hybrid workflows:

```php
Bus::chain([
    new PrepareExport($report),
    Bus::batch([
        new ExportChunk($report, 0),
        new ExportChunk($report, 1),
        new ExportChunk($report, 2),
    ]),
    new MergeExportChunks($report),
])->dispatch();

```

This pattern — prepare, fan-out in parallel, then merge — is a clean map-reduce for queue workers.

---

### Rate-Limited Job Middleware

Throttling at the job level is far more reliable than sleeping inside `handle()`. Laravel's `RateLimited` middleware uses the cache to enforce limits across all workers.

```php
use Illuminate\Queue\Middleware\RateLimited;

class SyncToStripe implements ShouldQueue
{
    public function middleware(): array
    {
        return [new RateLimited('stripe-sync')];
    }
}

```

Define the limiter in a service provider:

```php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('stripe-sync', fn () =>
    Limit::perMinute(60)->response(fn () =>
        // job is released back to the queue automatically
        null
    )
);

```

When the limit is hit, the job is released back with a calculated backoff — no wasted worker time, no dropped requests.

For per-tenant limits, key the limiter dynamically:

```php
public function middleware(): array
{
    return [new RateLimited('stripe-sync:' . $this->tenantId)];
}

```

---

### Takeaways

- Use `Bus::batch()` for parallel fan-out; always add `Batchable` and check for cancellation.
- Use `Bus::chain()` for sequential pipelines where order and dependency matter.
- Nest a batch inside a chain for map-reduce style workflows.
- Throttle third-party API jobs with `RateLimited` middleware — never `sleep()`.
- Key rate limiters per tenant or resource to avoid cross-tenant interference.
- Monitor batch progress via `$batch->id` and expose it to your UI for long-running operations.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Fjob-batching-chaining-and-rate-limited-middleware-in-laravel-queues&text=Job+Batching%2C+Chaining%2C+and+Rate-Limited+Middleware+in+Laravel+Queues) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Fjob-batching-chaining-and-rate-limited-middleware-in-laravel-queues) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  What happens to a chain if one job in a nested batch fails?        By default, a single failure in a batch cancels the batch and prevents subsequent chain steps from running. Use `-&gt;allowFailures()` on the batch if you want remaining batch jobs to continue, but note the chain's `catch` callback will still fire if the batch itself is marked as failed. 

      Q02  Does rate-limited middleware work across multiple Horizon workers?        Yes. The `RateLimited` middleware uses your configured cache driver as the shared counter, so limits are enforced globally across all worker processes and servers — as long as they share the same cache backend (Redis is recommended). 

      Q03  Can I track batch progress in a Filament or Livewire UI?        Yes. Store the batch ID in your database or session after dispatching, then poll `Bus::findBatch($id)` on a Livewire component using a `wire:poll` directive. The `$batch-&gt;progress()` method returns an integer 0–100 you can bind directly to a progress bar. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Contextual Binding and Method Injection in Laravel's Service Container](https://cdn.msaied.com/170/396bdacb111411b734f3018920214c20.png) laravel service-container dependency-injection 

### Contextual Binding and Method Injection in Laravel's Service Container

Go beyond basic dependency injection. Learn how Laravel's contextual binding, tagged services, and method inje...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/contextual-binding-and-method-injection-in-laravels-service-container) [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Chaos](https://cdn.msaied.com/169/5efebaf19646869da7c6064340c7d09f.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Chaos

JSONB columns are powerful but easy to misuse. Learn how to index, query, and cast PostgreSQL JSONB data in La...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-chaos) [ ![Cursor Pagination at Scale with Laravel Lazy Collections and Chunked Iteration](https://cdn.msaied.com/168/72d002a6bc12d3a7278c39e54b7ce46a.png) laravel eloquent performance 

### Cursor Pagination at Scale with Laravel Lazy Collections and Chunked Iteration

Offset pagination collapses under large datasets. Learn how to combine cursor pagination, lazy collections, an...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 14 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/cursor-pagination-at-scale-with-laravel-lazy-collections-and-chunked-iteration) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
