adilazhari/laravel-idempotency

A Laravel package providing idempotent request handling.

Maintainers

Package info

github.com/AdilAzhari/laravel-idempotency

pkg:composer/adilazhari/laravel-idempotency

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 2

v1.0.0 2026-07-26 09:47 UTC

This package is auto-updated.

Last update: 2026-07-26 10:55:12 UTC


README

Laravel Idempotency

Laravel Idempotency Logo

Laravel Idempotency

Framework-native HTTP idempotency for Laravel applications.

Tests

PHP

Laravel

License

Why Another Idempotency Package?

Laravel applications commonly implement idempotency in an ad-hoc manner, often coupling request replay, persistence, and locking into application code.

Laravel Idempotency provides a reusable, framework-native solution built around explicit contracts and interchangeable components, allowing storage, fingerprinting, and locking strategies to evolve independently.

Laravel Idempotency

Laravel Idempotency provides a framework-native solution for ensuring that identical HTTP requests are executed exactly once.

Using an Idempotency-Key, the package stores the original HTTP response and automatically replays it for subsequent identical requests, eliminating accidental duplicate operations caused by retries or network failures.

It is particularly useful for:

  • Payment processing
  • Subscription management
  • Order creation
  • Inventory updates
  • Account changes
  • External API integrations
  • Webhook processing

Why Idempotency?

Distributed systems are unreliable.

Clients retry requests.

Browsers retry requests.

Mobile applications automatically retry after network interruptions.

Load balancers and reverse proxies may resend requests.

Without idempotency, a single retry can unintentionally create duplicate:

  • Payments
  • Orders
  • Invoices
  • Subscriptions
  • Shipments
  • Customer accounts

Laravel Idempotency guarantees that the same logical operation executes only once while returning the original response for every identical retry.

Features

  • HTTP idempotency middleware
  • Atomic request locking
  • Automatic response replay
  • SHA-256 request fingerprinting
  • Configurable response expiration
  • Multiple storage drivers
    • Array
    • Cache
    • Redis
    • Database
  • Database pruning command
  • Custom fingerprint strategies
  • Custom storage implementations
  • Laravel-native dependency injection
  • Fully tested
  • Production-ready

Requirements

  • PHP 8.5+
  • Laravel 13+

For production deployments, use a shared storage backend such as Redis or the Database driver.

Installation

Install the package using Composer.

composer require adilazhari/laravel-idempotency

Laravel automatically discovers the package.

Publish the configuration file:

php artisan vendor:publish --tag=idempotency-config

If you intend to use the database storage driver, also publish the migration:

php artisan vendor:publish --tag=idempotency-migrations

php artisan migrate

Quick Start

Protect any write endpoint using the middleware.

use AdilAzhari\LaravelIdempotency\Http\Middleware\IdempotencyMiddleware;
use Illuminate\Support\Facades\Route;

Route::post('/payments', CreatePaymentController::class)
    ->middleware(IdempotencyMiddleware::class);

Clients simply provide an idempotency key.

POST /payments
Idempotency-Key: payment-123

{
    "amount": 1000,
    "currency": "MYR"
}

The first request executes normally.

Subsequent identical requests return the previously stored response without executing the controller again.

How It Works

                 Client
                    │
                    ▼
       Idempotency Middleware
                    │
                    ▼
     Generate Request Fingerprint
                    │
                    ▼
         Acquire Execution Lock
                    │
        ┌───────────┴───────────┐
        │                       │
        ▼                       ▼
 Existing Response?          No Response
        │                       │
      Yes                       ▼
        │               Execute Controller
        │                       │
        ▼                       ▼
 Replay Stored Response   Store HTTP Response
        │                       │
        └───────────┬───────────┘
                    ▼
             Release Lock
                    │
                    ▼
             Return Response

Request Matching

Two requests are considered identical when all of the following match:

  • Idempotency key
  • HTTP method
  • Request path
  • Query parameters
  • Parsed request body

The default implementation uses the built-in Sha256RequestFingerprinter.

For example:

POST /payments

{
    "amount":1000
}

and

POST /payments

{
    "amount":2000
}

are treated as different requests, even if they share the same idempotency key.

If the same key is reused for different request data, an IdempotencyConflictException is thrown.

Storage Drivers

Laravel Idempotency supports multiple persistence backends.

Driver Recommended Usage
Array Testing
Cache Default
Redis Distributed applications
Database Durable persistence

Select the active driver through configuration.

IDEMPOTENCY_STORE=cache

Switching drivers requires no application code changes.

Configuration

return [

    'driver' => env('IDEMPOTENCY_STORE', 'cache'),

    'stores' => [

        'cache' => [
            'driver' => 'cache',
        ],

        'array' => [
            'driver' => 'array',
        ],

        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
            'prefix' => 'idempotency:',
        ],

        'database' => [
            'driver' => 'database',
        ],

    ],

    'header' => 'Idempotency-Key',

    'lock' => [
        'seconds' => 10,
    ],

    'expiration' => 86400,

];
Option Description
driver Active storage driver
header HTTP header containing the idempotency key
lock.seconds Maximum lock duration
expiration Lifetime of stored responses

The lock duration should comfortably exceed the execution time of your protected endpoints.

Extending

Laravel Idempotency is built around a small set of contracts, allowing individual components to be replaced without modifying the package.

Custom Request Fingerprinting

Bind your own implementation of the RequestFingerprinter contract.

use AdilAzhari\LaravelIdempotency\Contracts\RequestFingerprinter;

$this->app->bind(
    RequestFingerprinter::class,
    CustomRequestFingerprinter::class,
);

Possible use cases include:

  • Ignoring selected request fields
  • Including additional HTTP headers
  • Supporting custom hashing algorithms
  • Multi-tenant request isolation

Custom Storage

Implement the IdempotencyStore contract to persist responses anywhere.

use AdilAzhari\LaravelIdempotency\Contracts\IdempotencyStore;

$this->app->bind(
    IdempotencyStore::class,
    CustomIdempotencyStore::class,
);

Possible implementations include:

  • Redis
  • Database
  • DynamoDB
  • MongoDB
  • Amazon S3
  • External persistence services

Custom Locking

Locking is also replaceable.

use AdilAzhari\LaravelIdempotency\Contracts\IdempotencyLock;

$this->app->bind(
    IdempotencyLock::class,
    CustomIdempotencyLock::class,
);

This allows integration with:

  • Redis locks
  • Database locks
  • Distributed lock services
  • Cloud-native coordination systems

Storage Drivers

Cache Driver

The Cache driver is the default storage implementation.

IDEMPOTENCY_STORE=cache

It stores responses using Laravel's configured cache store and is suitable for most applications.

Array Driver

The Array driver stores responses in memory.

IDEMPOTENCY_STORE=array

This driver is intended for:

  • Unit tests
  • Local development
  • Temporary storage

Because data exists only in memory, it should not be used in production.

Redis Driver

The Redis driver stores responses directly in Redis.

IDEMPOTENCY_STORE=redis

Configuration:

'redis' => [

    'driver' => 'redis',

    'connection' => 'default',

    'prefix' => 'idempotency:',

],

This driver is recommended for distributed deployments where multiple application instances must share idempotency records.

Database Driver

The Database driver persists responses using Eloquent.

IDEMPOTENCY_STORE=database

Before using it, publish the migration:

php artisan vendor:publish --tag=idempotency-migrations

php artisan migrate

The database driver provides durable storage that survives cache flushes and application restarts.

Pruning Expired Records

Expired database records can be removed using the built-in Artisan command.

php artisan idempotency:prune

Scheduling the command is recommended.

use Illuminate\Support\Facades\Schedule;

Schedule::command('idempotency:prune')
    ->daily();

Design Principles

Laravel Idempotency is intentionally built around a small set of principles.

  • Framework-native integration
  • Explicit contracts
  • Predictable behaviour
  • Simple public API
  • Testability
  • Extensibility

Testing

Run the complete test suite.

composer test

The package includes comprehensive unit and feature tests covering:

  • Request replay
  • Conflict detection
  • Request fingerprinting
  • Atomic locking
  • Cache storage
  • Redis storage
  • Database storage
  • Array storage
  • Middleware integration
  • Service provider registration
  • Configuration
  • Console commands

Documentation

Additional documentation is available in the docs directory.

  • Architecture
  • Storage Drivers
  • Custom Fingerprinting
  • Custom Storage Drivers
  • Custom Locking
  • Contributing Guide

Roadmap

  • ✅ Laravel 13 support
  • ✅ HTTP idempotency middleware
  • ✅ Atomic request locking
  • ✅ SHA-256 request fingerprinting
  • ✅ Cache storage driver
  • ✅ Array storage driver
  • ✅ Redis storage driver
  • ✅ Database storage driver
  • ✅ Configurable storage drivers
  • ✅ Database pruning command
  • 🚧 Additional storage adapters

Contributing

Contributions are welcome.

If you discover a bug or would like to propose a new feature, please open an issue before submitting a pull request so the implementation can be discussed first.

When contributing:

  • Follow the existing coding style.
  • Include tests for new functionality.
  • Ensure composer test passes before opening a pull request.

License

Laravel Idempotency is open-source software licensed under the MIT License.