Rate Limiter

Backed by Services\Cache — any of its three drivers works, so the limit is consistent across a multi-node deployment whenever Cache uses the database or Redis driver.

Wiring it

final class RateLimiter extends \Monad\Clarity\Middlewares\RateLimiter
{
    public function __construct(int $maxAttempts = 10, int $windowSeconds = 60)
    {
        parent::__construct(
            cache: new Cache(Cache::DRIVER_FILE, path: PATH['cache']), // swap for database/Redis in production
            maxAttempts: $maxAttempts,
            windowSeconds: $windowSeconds,
        );
    }
}

As a pipeline middleware

Route::get('/api/orders', [OrderController::class, 'index'])->middleware(new RateLimiter(maxAttempts: 60, windowSeconds: 60));

A real 3-request sequence against a 2-attempt limit:

1st request: 200, X-RateLimit-Remaining: 1
2nd request: 200, X-RateLimit-Remaining: 0
3rd request: 429, Retry-After: 60

Limits by $request->ip() by default — override resolveKey() to key by an authenticated user id or API token instead.

Called directly — for a specific identifier, not the whole route

Login and password reset rate-limit one identifier (an email), not every request to the route — call these directly instead of registering as pipeline middleware:

$limiter->attempt(string $key): bool          // records one attempt, reports whether within limit
$limiter->remaining(string $key): int         // without recording a new attempt
$limiter->availableInSeconds(string $key): int
$limiter->clear(string $key): void            // e.g. after a successful login

This is exactly how Authentication's attempt() uses it internally.

Known, accepted limitations

  • Not atomic. Read-then-write, since PSR-16 has no atomic increment — under concurrent requests against the same key, two requests can both read the same count and both pass. A strong deterrent, not a hard guarantee.
  • Fixed-window boundary burst. A caller can send the limit at the tail of one window and the limit again at the head of the next — up to 2x the configured limit within a short span straddling the boundary.

Next steps

  • Cache — the backing store and its three drivers.
  • Authentication — the required login-throttling call site.