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.