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

Yii2 Ratelimiter Advanced Laravel Package

thamtech/yii2-ratelimiter-advanced

Advanced rate limiter filter for Yii2 using a leaky-bucket algorithm. Define multiple independent limits per action and identifier (IP, user ID, etc.), store allowance/timestamp automatically, customize responses (429, events, headers, callbacks), and support Retry-After.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require thamtech/yii2-ratelimiter-advanced
    

    Add to your Yii2 config/web.php:

    'components' => [
        'rateLimiter' => [
            'class' => 'thamtech\ratelimiter\RateLimiter',
            'storage' => 'cache', // or 'db' for database-backed storage
        ],
    ],
    
  2. First Use Case: Basic Rate Limiting Apply to a controller action:

    use thamtech\ratelimiter\RateLimiter;
    
    public function actionExample(RateLimiter $rateLimiter) {
        $key = 'user:123:action:example';
        if (!$rateLimiter->allow($key, 10, 60)) { // 10 requests per 60 seconds
            throw new \yii\web\HttpException(429, 'Too many requests');
        }
        // Action logic...
    }
    
  3. Key Resources

    • README.md for storage options (cache/db).
    • RateLimiter class docs for methods like allow(), reset(), and getRemaining().

Implementation Patterns

Common Workflows

  1. User-Based Rate Limiting

    $key = 'user:' . Yii::$app->user->id . ':api:endpoint';
    if (!$rateLimiter->allow($key, 5, 30)) { // 5 requests per 30 seconds
        return $this->asJson(['error' => 'Rate limit exceeded'], 429);
    }
    
  2. IP-Based Rate Limiting (Fallback)

    $key = 'ip:' . Yii::$app->request->userIP . ':login';
    if (!$rateLimiter->allow($key, 3, 60)) {
        throw new \yii\web\HttpException(429, 'Too many login attempts');
    }
    
  3. Dynamic Rate Limits via Config Define limits in config/params.php:

    'rateLimits' => [
        'api:create' => ['max' => 10, 'window' => 60],
        'api:read'   => ['max' => 100, 'window' => 60],
    ],
    

    Use in controller:

    $limit = Yii::$app->params['rateLimits']['api:create'];
    $key = 'user:' . Yii::$app->user->id . ':api:create';
    if (!$rateLimiter->allow($key, $limit['max'], $limit['window'])) { ... }
    
  4. Middleware Integration Create a middleware to enforce global limits:

    namespace app\middleware;
    use thamtech\ratelimiter\RateLimiter;
    
    class RateLimitMiddleware {
        public function __construct(RateLimiter $rateLimiter) {
            $this->rateLimiter = $rateLimiter;
        }
    
        public function beforeAction($action) {
            $key = 'ip:' . Yii::$app->request->userIP . ':' . $action->id;
            if (!$this->rateLimiter->allow($key, 100, 60)) {
                throw new \yii\web\HttpException(429);
            }
        }
    }
    

    Register in config/web.php:

    'components' => [
        'request' => [
            'enableCsrfValidation' => false, // Optional: Disable if using middleware
        ],
    ],
    'middleware' => [
        'rateLimit' => ['class' => app\middleware\RateLimitMiddleware::class],
    ],
    
  5. Database Storage for Persistence Configure in web.php:

    'rateLimiter' => [
        'class' => 'thamtech\ratelimiter\RateLimiter',
        'storage' => [
            'class' => 'thamtech\ratelimiter\storage\DbStorage',
            'tableName' => '{{%rate_limits}}',
        ],
    ],
    

    Run migrations (check migrations/ in the package for schema).


Gotchas and Tips

Pitfalls

  1. Cache vs. Database Storage

    • Cache: Fast but volatile (resets on cache flush/restart).
    • Database: Persistent but slower (adds DB overhead). Use for critical paths like login attempts.
  2. Key Collisions

    • Avoid overly generic keys (e.g., 'user:123'). Specify actions/resources:
      // Bad: 'user:123' (limits all actions for user)
      // Good: 'user:123:api:create' (scopes to one endpoint)
      
  3. Concurrency Issues

    • The package uses token bucket algorithm by default. For high-traffic apps, consider:
      $rateLimiter->allow($key, 100, 60, 'fixed'); // Fixed window (simpler but less precise)
      
  4. Memory Leaks

    • Cache-backed storage can bloat memory. Set TTLs explicitly:
      $rateLimiter->allow($key, 10, 60, null, 3600); // 1-hour TTL for the key
      
  5. Yii2 Dependency

    • Assumes Yii2’s Cache and Db components. Custom storage requires extending thamtech\ratelimiter\storage\StorageInterface.

Debugging Tips

  1. Check Remaining Tokens

    $remaining = $rateLimiter->getRemaining($key);
    Yii::debug("Remaining tokens: $remaining");
    
  2. Reset Limits Manually

    $rateLimiter->reset($key); // Reset a specific key
    $rateLimiter->resetAll(); // Clear all limits (use cautiously!)
    
  3. Log Rate Limit Events

    if (!$rateLimiter->allow($key, 10, 60)) {
        Yii::warning("Rate limit exceeded for $key");
        throw new \yii\web\HttpException(429);
    }
    
  4. Monitor Storage

    • For DbStorage, check the {{%rate_limits}} table for stale entries:
      SELECT * FROM {{%rate_limits}} WHERE expires_at < NOW();
      

Extension Points

  1. Custom Storage Implement thamtech\ratelimiter\storage\StorageInterface:

    class RedisStorage implements StorageInterface {
        public function get($key) { ... }
        public function set($key, $value, $ttl) { ... }
        public function delete($key) { ... }
    }
    

    Use in config:

    'storage' => ['class' => app\storage\RedisStorage::class],
    
  2. Event-Driven Limits Extend the RateLimiter class to trigger events:

    class ExtendedRateLimiter extends RateLimiter {
        public function allow($key, $max, $window, $algorithm = null) {
            $allowed = parent::allow($key, $max, $window, $algorithm);
            if (!$allowed) {
                Yii::$app->trigger('rateLimitExceeded', [$key]);
            }
            return $allowed;
        }
    }
    
  3. Dynamic Rate Adjustment Override getRate() to fetch limits from an API or DB:

    $rateLimiter->setRateResolver(function($key) {
        return ['max' => 5, 'window' => 60]; // Or fetch from DB/API
    });
    
  4. Header-Based Enforcement Add headers for API clients:

    if (!$rateLimiter->allow($key, 100, 60)) {
        Yii::$app->response->getHeaders()->add(
            'X-RateLimit-Limit', 100
        );
        Yii::$app->response->getHeaders()->add(
            'X-RateLimit-Remaining', 0
        );
        throw new \yii\web\HttpException(429);
    }
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport