Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Idempotency Laravel Package

webrek/laravel-idempotency

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require webrek/laravel-idempotency
    php artisan vendor:publish --provider="Webrek\Idempotency\IdempotencyServiceProvider"
    

    This publishes the config file (config/idempotency.php) and migration (database/migrations/xxxx_create_idempotency_keys_table.php).

  2. Run Migration:

    php artisan migrate
    

    Creates the idempotency_keys table to store keys and responses.

  3. First Use Case: Apply the middleware to a route handling state-changing requests (e.g., POST /orders):

    Route::post('/orders', [OrderController::class, 'store'])
        ->middleware('idempotency');
    

    Clients must include the Idempotency-Key header (e.g., UUID) in their request:

    POST /orders HTTP/1.1
    Idempotency-Key: abc123...
    
  4. Test the Flow:

    • First request: Processes normally, stores the response in the idempotency_keys table.
    • Second identical request (same key): Returns the cached response (HTTP 200) without reprocessing.

Implementation Patterns

Core Workflow

  1. Middleware Integration:

    • Apply idempotency middleware to routes where idempotency is required (e.g., POST, PUT, PATCH).
    • Exclude routes where idempotency isn’t needed (e.g., GET endpoints).
  2. Key Generation:

    • Clients generate a unique key (e.g., UUID, hash of request body + timestamp).
    • Example (JavaScript):
      const key = crypto.randomUUID();
      fetch('/orders', {
        method: 'POST',
        headers: { 'Idempotency-Key': key },
        body: JSON.stringify({ product_id: 123 })
      });
      
  3. Response Handling:

    • First Request: Processes the request, stores the response (status code, headers, body) in the database.
    • Subsequent Requests: Returns the cached response immediately (no reprocessing).
    • Customize the cached response with a response() helper:
      return response()->idempotent($request, $response);
      
  4. Cleanup:

    • Automatically expires keys after idempotency.ttl (default: 1 day) via Laravel’s scheduler.
    • Manually delete keys with:
      \Webrek\Idempotency\Facades\Idempotency::delete($key);
      

Advanced Patterns

  1. Conditional Idempotency: Use middleware groups or route conditions to apply idempotency selectively:

    Route::post('/orders/{id}', [OrderController::class, 'update'])
        ->middleware(['idempotency', 'throttle:60']);
    
  2. Custom Storage: Extend the IdempotencyRepository interface to use Redis or another driver:

    // config/idempotency.php
    'driver' => 'redis',
    
  3. Webhook Handling: For webhooks (e.g., Stripe), use idempotency to avoid duplicate processing:

    Route::post('/webhooks/stripe', [WebhookController::class, 'handle'])
        ->middleware('idempotency');
    
  4. Testing: Mock the idempotency middleware in tests:

    $this->withHeaders(['Idempotency-Key' => 'test-key'])
         ->post('/orders', [...])
         ->assertSuccessful();
    

Gotchas and Tips

Pitfalls

  1. Key Collisions:

    • If two identical requests use the same key (e.g., client reuses a key), the second request will overwrite the first.
    • Fix: Ensure clients generate unique keys per request (e.g., UUID + timestamp).
  2. Large Responses:

    • Storing large responses (e.g., JSON APIs) in the database may bloat the idempotency_keys table.
    • Fix: Use config('idempotency.store_body') = false to store only metadata (status code, headers).
  3. Race Conditions:

    • Between the first request processing and the second request hitting the cache, a race condition could occur.
    • Fix: Use database transactions or optimistic locking in the middleware.
  4. TTL Misconfiguration:

    • Setting idempotency.ttl too low (e.g., 5 minutes) may cause legitimate retries to fail.
    • Fix: Set a reasonable TTL (e.g., 1 day for orders, 1 hour for payments).
  5. Middleware Order:

    • Place idempotency before authentication/authorization middleware to avoid processing the request twice.
    • Example:
      Route::post('/orders', [...])
          ->middleware(['idempotency', 'auth:sanctum']);
      

Debugging Tips

  1. Check the Database:

    • Verify keys are stored/expired correctly:
      SELECT * FROM idempotency_keys WHERE key = 'abc123...';
      
  2. Log Middleware Events:

    • Enable logging in config/idempotency.php:
      'log' => env('IDEMPOTENCY_LOG', true),
      
    • Check Laravel logs for Idempotency entries.
  3. Test Edge Cases:

    • Simulate retries with tools like Postman or [curl]:
      curl -X POST -H "Idempotency-Key: abc123..." http://your-api/orders
      
    • Verify the second request returns the cached response.

Extension Points

  1. Custom Key Validation: Override the default key validation (e.g., enforce regex patterns):

    // app/Providers/IdempotencyServiceProvider.php
    public function boot()
    {
        Idempotency::validateKeyUsing(function ($key) {
            return preg_match('/^[a-f0-9]{32}$/', $key);
        });
    }
    
  2. Event Listeners: Listen for idempotency events (e.g., IdempotencyStored, IdempotencyRetrieved):

    // app/Providers/EventServiceProvider.php
    protected $listen = [
        \Webrek\Idempotency\Events\IdempotencyStored::class => [
            \App\Listeners\LogIdempotency::class,
        ],
    ];
    
  3. API Versioning: Scope keys by API version to avoid conflicts across versions:

    // In middleware
    $key = "v2_{$request->header('Idempotency-Key')}";
    
  4. Rate Limiting: Combine with Laravel’s throttle middleware to limit retries:

    Route::post('/orders', [...])
        ->middleware(['idempotency', 'throttle:10,1']);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codraw/entity-migrator
codraw/doctrine-extra
codraw/aws-tool-kit
codraw/validator
codraw/workflow
codraw/open-api
codraw/cron-job
codraw/process
codraw/log
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony