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

Idempotency Bundle Laravel Package

conejerock/idempotency-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require conejerock/idempotency-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        ConejeRock\IdempotencyBundle\IdempotencyBundle::class => ['all' => true],
    ];
    
  2. Basic Configuration Publish the default config:

    php bin/console idempotency:install
    

    Update config/packages/idempotency.yaml to define:

    • idempotency_key_header (e.g., X-Idempotency-Key)
    • idempotency_key_query_param (e.g., idempotency_key)
    • storage (e.g., database, redis, or memory)
  3. First Use Case Enable idempotency for a controller action by adding the #[Idempotency] attribute:

    use ConejeRock\IdempotencyBundle\Attribute\Idempotency;
    
    #[Idempotency]
    public function createOrder(Request $request): JsonResponse
    {
        // Your logic here
    }
    

    The bundle will automatically:

    • Extract the key from headers/query params.
    • Store the request/response pair in the configured storage.
    • Return cached responses for duplicate keys.

Implementation Patterns

Workflows

  1. Request-Level Idempotency Use the #[Idempotency] attribute on controller methods to enforce idempotency for specific endpoints:

    #[Idempotency]
    public function processPayment(Request $request): Response
    {
        // Idempotent logic (e.g., charge a payment)
    }
    
  2. Custom Key Extraction Override the default key extraction logic by implementing IdempotencyKeyExtractorInterface:

    use ConejeRock\IdempotencyBundle\Extractor\IdempotencyKeyExtractorInterface;
    
    class CustomKeyExtractor implements IdempotencyKeyExtractorInterface
    {
        public function extract(Request $request): ?string
        {
            return $request->get('custom_param');
        }
    }
    

    Register it in config/packages/idempotency.yaml:

    idempotency:
        extractor: App\Service\CustomKeyExtractor
    
  3. Conditional Idempotency Use the #[Idempotency] attribute with options:

    #[Idempotency(
        ttl: 3600, // 1-hour cache
        storage: 'redis',
        ignoreHeaders: ['Authorization']
    )]
    public function updateProfile(Request $request): Response
    {
        // ...
    }
    
  4. Event-Based Extensions Listen to idempotency.key.generated and idempotency.response.served events to log or modify behavior:

    use ConejeRock\IdempotencyBundle\Event\IdempotencyEvents;
    
    $eventDispatcher->addListener(IdempotencyEvents::KEY_GENERATED, function ($event) {
        // Log the generated key
    });
    

Integration Tips

  • Laravel-Specific Setup Since this is a Symfony bundle, use spatie/laravel-symfony-bundle to bridge compatibility:

    composer require spatie/laravel-symfony-bundle
    

    Register the bundle in config/app.php under Symfony\Bridge\Laravel\ServiceProvider.

  • Database Storage For Laravel, configure the database storage in idempotency.yaml:

    idempotency:
        storage: database
        database:
            table: idempotency_keys
            connection: mysql
    

    Run migrations:

    php artisan vendor:publish --provider="ConejeRock\IdempotencyBundle\Database\IdempotencyDatabaseProvider"
    php artisan migrate
    
  • API Gateway Use Case Combine with Laravel’s throttle middleware to limit rate + idempotency:

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

Gotchas and Tips

Pitfalls

  1. Key Collisions

    • Issue: If two requests generate the same key (e.g., UUID collisions), the second request will silently return the first response.
    • Fix: Use a stronger key generation strategy (e.g., combine request body hash with a timestamp):
      idempotency:
          key_generator: ConejeRock\IdempotencyBundle\Generator\CompositeKeyGenerator
      
  2. Storage Locking

    • Issue: Concurrent requests with the same key may cause race conditions in memory storage.
    • Fix: Use redis or database storage for production:
      idempotency:
          storage: redis
          redis:
              client: predis
      
  3. Attribute Overrides

    • Issue: Method-level #[Idempotency] attributes override global config.
    • Fix: Prefer global config for consistency; use method-level only for exceptions.
  4. Request Body Parsing

    • Issue: Large request bodies (e.g., file uploads) may bloat storage.
    • Fix: Exclude body from key generation for large payloads:
      idempotency:
          ignore_body: true
      

Debugging

  • Log Generated Keys Enable debug mode in idempotency.yaml:

    idempotency:
        debug: true
    

    Check logs for idempotency.key.generated and idempotency.response.served events.

  • Clear Cache Manually purge idempotency keys:

    php bin/console idempotency:clear
    

    For Laravel, use:

    php artisan idempotency:clear
    

Extension Points

  1. Custom Storage Implement IdempotencyStorageInterface:

    use ConejeRock\IdempotencyBundle\Storage\IdempotencyStorageInterface;
    
    class LaravelCacheStorage implements IdempotencyStorageInterface
    {
        public function store(string $key, array $data, int $ttl): bool
        {
            Cache::put($key, $data, $ttl);
            return true;
        }
    
        public function retrieve(string $key): ?array
        {
            return Cache::get($key);
        }
    
        public function delete(string $key): bool
        {
            return Cache::forget($key);
        }
    }
    

    Register it in config:

    idempotency:
        storage: App\Service\LaravelCacheStorage
    
  2. Response Transformation Override the default response caching logic by extending IdempotencyListener:

    use ConejeRock\IdempotencyBundle\EventListener\IdempotencyListener;
    
    class CustomIdempotencyListener extends IdempotencyListener
    {
        protected function transformResponse(Response $response): array
        {
            // Custom serialization logic
            return ['data' => $response->getContent()];
        }
    }
    

    Bind it in services.yaml:

    services:
        ConejeRock\IdempotencyBundle\EventListener\IdempotencyListener:
            class: App\EventListener\CustomIdempotencyListener
    
  3. Key Validation Add validation logic via IdempotencyKeyValidatorInterface:

    use ConejeRock\IdempotencyBundle\Validator\IdempotencyKeyValidatorInterface;
    
    class RegexKeyValidator implements IdempotencyKeyValidatorInterface
    {
        public function isValid(string $key): bool
        {
            return preg_match('/^[a-f0-9]{32}$/', $key);
        }
    }
    

    Configure in idempotency.yaml:

    idempotency:
        validator: App\Validator\RegexKeyValidator
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor