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 for Laravel Queues        On this page       1. [  Job Batching, Chaining, and Rate-Limited Middleware ](#job-batching-chaining-and-rate-limited-middleware)
2. [  Batching: Fan-Out With a Finish Line ](#batching-fan-out-with-a-finish-line)
3. [  Adding Jobs to a Running Batch ](#adding-jobs-to-a-running-batch)
4. [  Chaining: Sequential Pipelines With Error Isolation ](#chaining-sequential-pipelines-with-error-isolation)
5. [  Rate-Limited Job Middleware ](#rate-limited-job-middleware)
6. [  Takeaways ](#takeaways)

  ![Job Batching, Chaining, and Rate-Limited Middleware for Laravel Queues](https://cdn.msaied.com/414/154bd945ee1677f140cd1ea4132e5b66.png)

  #laravel   #queues   #jobs   #async  

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

     12 Jul 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  ](#job-batching-chaining-and-rate-limited-middleware)
2. [  02   Batching: Fan-Out With a Finish Line  ](#batching-fan-out-with-a-finish-line)
3. [  03   Adding Jobs to a Running Batch  ](#adding-jobs-to-a-running-batch)
4. [  04   Chaining: Sequential Pipelines With Error Isolation  ](#chaining-sequential-pipelines-with-error-isolation)
5. [  05   Rate-Limited Job Middleware  ](#rate-limited-job-middleware)
6. [  06   Takeaways  ](#takeaways)

 Job Batching, Chaining, and Rate-Limited Middleware
---------------------------------------------------

Laravel's queue system is deceptively powerful once you move past `dispatch()`. Three features — batching, chaining, and rate-limited middleware — compose into workflows that are resilient, observable, and polite to external APIs. This article shows how to wire them together correctly.

---

Batching: Fan-Out With a Finish Line
------------------------------------

A batch dispatches many jobs in parallel and lets you react when they all finish (or any one fails).

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

$batch = Bus::batch([
    new ProcessInvoice($invoice1),
    new ProcessInvoice($invoice2),
    new ProcessInvoice($invoice3),
])
->then(fn (Batch $batch) => Report::markComplete($batch->id))
->catch(fn (Batch $batch, Throwable $e) => Report::markFailed($batch->id, $e->getMessage()))
->finally(fn (Batch $batch) => Cache::forget("batch:{$batch->id}"))
->onQueue('invoices')
->dispatch();

session(['batch_id' => $batch->id]);

```

The `then` callback fires only when **all** jobs succeed. `catch` fires on the **first** failure but the batch continues processing remaining jobs by default. Call `$batch->cancel()` inside `catch` to halt everything.

### Adding Jobs to a Running Batch

Jobs can push siblings into their own batch — useful for recursive fan-out:

```php
public function handle(): void
{
    $children = $this->fetchChildIds();

    if ($children->isNotEmpty()) {
        $this->batch()->add(
            $children->map(fn ($id) => new ProcessChild($id))->all()
        );
    }
}

```

This keeps the batch open until every dynamically added job also completes.

---

Chaining: Sequential Pipelines With Error Isolation
---------------------------------------------------

Chaining runs jobs one after another, stopping on failure.

```php
Bus::chain([
    new ValidateOrder($order),
    new ChargePayment($order),
    new SendConfirmationEmail($order),
])
->catch(function (Throwable $e) use ($order) {
    $order->markFailed($e->getMessage());
    Notification::send($order->owner, new OrderFailedNotification($order));
})
->dispatch();

```

> **Gotcha:** the `catch` closure is serialized. Avoid injecting large objects — use IDs and re-query inside the closure.

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 FinalizeExport($report),
])->dispatch();

```

The chain pauses at the batch step until all chunks complete, then continues to `FinalizeExport`.

---

Rate-Limited Job Middleware
---------------------------

When jobs call external APIs you need to throttle throughput without blocking workers. Job middleware is the right tool — not `sleep()`.

```php
namespace App\Jobs\Middleware;

use Illuminate\Support\Facades\RateLimiter;

class ThrottleStripeApi
{
    public function handle(object $job, callable $next): void
    {
        RateLimiter::attempt(
            key: 'stripe-api',
            maxAttempts: 100,
            callback: fn () => $next($job),
            decaySeconds: 60,
        ) || $job->release(10); // re-queue after 10 s if limit hit
    }
}

```

Attach it to any job:

```php
public function middleware(): array
{
    return [new ThrottleStripeApi()];
}

```

Laravel ships `Illuminate\Queue\Middleware\RateLimited` for Redis-backed limiting with the named limiter API:

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

public function middleware(): array
{
    return [new RateLimited('stripe')];
}

```

Define the limiter in `AppServiceProvider`:

```php
RateLimiter::for('stripe', fn () =>
    Limit::perMinute(100)->by('stripe-global')
);

```

The middleware automatically releases the job back to the queue with a calculated delay, so workers stay busy processing other jobs instead of sleeping.

---

Takeaways
---------

- Use **batches** for parallel fan-out; use `then`/`catch`/`finally` for lifecycle hooks.
- Use **chains** for sequential workflows; keep `catch` closures lightweight and ID-based.
- Embed a batch inside a chain to get parallel middle steps with sequential bookends.
- Use **rate-limited middleware** — not `sleep()` — to throttle external API calls; workers stay productive.
- Named `RateLimiter` definitions keep throttle logic centralized and testable.
- Always test batch/chain behavior with `Queue::fake()` and assert on `Bus::assertBatched()` / `Bus::assertChained()`.

 Found this useful?

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

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

  3 questions  

     Q01  Can a batched job add more jobs to the same batch at runtime?        Yes. Inside a job's handle method, call `$this-&gt;batch()-&gt;add([...])` to push sibling jobs into the running batch. The batch stays open until all dynamically added jobs also complete. 

      Q02  What happens to remaining batch jobs when one fails?        By default the batch continues processing remaining jobs and the `catch` callback fires once. Call `$batch-&gt;cancel()` inside `catch` if you want to stop all pending jobs immediately. 

      Q03  Why use rate-limited middleware instead of sleeping inside the job?        Sleeping blocks the worker process for the entire sleep duration, wasting capacity. Rate-limited middleware releases the job back to the queue with a delay, freeing the worker to process other jobs in the meantime. 

  Continue reading

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

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

 [ ![DDD Value Objects and DTOs in Laravel Without the Bloat](https://cdn.msaied.com/450/41688537085b86f102fd8c219a35319f.png) laravel ddd php 

### DDD Value Objects and DTOs in Laravel Without the Bloat

Learn how to implement domain-driven value objects and data transfer objects in Laravel using PHP 8.3 readonly...

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

 21 Jul 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/ddd-value-objects-and-dtos-in-laravel-without-the-bloat) [ ![Laravel + Python FastAPI: Image OCR Demo](https://cdn.msaied.com/447/5f3d87146fbf19957973cbc88c6c0155.png) Laravel Python FastAPI 

### Laravel + Python FastAPI: Image OCR Demo

Learn how to call a Python image OCR script from Laravel using FastAPI as the bridge. This premium tutorial wa...

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

 20 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-python-fastapi-image-ocr-demo) [ ![Scaffold Laravel Packages with the `laravel package` Command in Installer v5.31.0](https://cdn.msaied.com/449/522e744d3c7bae1f214c179a83ae5b88.png) Laravel Installer Package Development CLI 

### Scaffold Laravel Packages with the `laravel package` Command in Installer v5.31.0

Laravel Installer v5.31.0 introduces a `laravel package` command to scaffold packages from the CLI, automated...

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

 20 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/scaffold-laravel-packages-with-the-laravel-package-command-in-installer-v5310) 

   [  ![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)
