Skip to main content

Job Middleware

truschery/idem provides a job middleware that prevents a queued job from executing more than once. If a job with the same key has already been processed, it will be skipped entirely — no exceptions, no side effects.

This is especially useful for jobs that perform irreversible operations such as sending emails, processing payments, or triggering external API calls.

Registering the Middleware

Import EnsureIdempotency and return it from the middleware() method on your Job class. Pass the idempotency key as a constructor argument:

use Truschery\Idem\Middleware\EnsureIdempotency;

class ProcessOrder implements ShouldQueue
{
public function __construct(
private string $orderId
) {}

public function middleware(): array
{
return [new EnsureIdempotency($this->orderId)];
}

public function handle(): void
{
// Will not execute if this orderId has been processed before
}
}

No interface or trait is required on the Job class.

Choosing a Key

The key should be a stable, unique identifier tied to the business entity being processed — not the job itself.

// ✅ Good — tied to the entity
new EnsureIdempotency($this->orderId)
new EnsureIdempotency($this->invoice->uuid)

// ❌ Bad — changes on every dispatch
new EnsureIdempotency(Str::uuid())

If the key changes on every dispatch, idempotency provides no protection. Pick an identifier that stays the same across retries.

Behavior on Repeated Execution

If a job is dispatched again with a key that has already been processed, it will be silently skipped. No exception is thrown, no handler is executed.

null is stored as the response — job middleware has no return value by design.