Implement rate limiting with Symfony RateLimiter component; configure sliding window, token bucket, and fixed window algorithms
/plugin marketplace add MakFly/superpowers-symfony/plugin install makfly-superpowers-symfony@MakFly/superpowers-symfonyThis skill inherits all available tools. When active, it can use any tool Claude has access to.
composer require symfony/rate-limiter
# config/packages/rate_limiter.yaml
framework:
rate_limiter:
# Anonymous API requests
anonymous_api:
policy: sliding_window
limit: 100
interval: '1 hour'
# Authenticated API requests
authenticated_api:
policy: sliding_window
limit: 1000
interval: '1 hour'
# Login attempts
login:
policy: fixed_window
limit: 5
interval: '15 minutes'
# Contact form
contact_form:
policy: fixed_window
limit: 3
interval: '1 hour'
# Expensive operations
export:
policy: token_bucket
limit: 10
rate: { interval: '1 hour', amount: 5 }
Simple count within time window:
login:
policy: fixed_window
limit: 5
interval: '15 minutes'
Smoother rate limiting, prevents burst at window edges:
api:
policy: sliding_window
limit: 100
interval: '1 hour'
Allows bursts while maintaining average rate:
export:
policy: token_bucket
limit: 10 # Bucket size (max burst)
rate:
interval: '1 hour' # Refill interval
amount: 5 # Tokens added per interval
<?php
// src/Controller/ApiController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Routing\Attribute\Route;
class ApiController extends AbstractController
{
public function __construct(
private RateLimiterFactory $authenticatedApiLimiter,
private RateLimiterFactory $anonymousApiLimiter,
) {}
#[Route('/api/data', methods: ['GET'])]
public function getData(Request $request): Response
{
// Choose limiter based on authentication
$limiter = $this->getUser()
? $this->authenticatedApiLimiter->create($this->getUser()->getUserIdentifier())
: $this->anonymousApiLimiter->create($request->getClientIp());
$limit = $limiter->consume();
if (!$limit->isAccepted()) {
return new JsonResponse(
['error' => 'Too many requests. Please try again later.'],
Response::HTTP_TOO_MANY_REQUESTS,
[
'X-RateLimit-Remaining' => $limit->getRemainingTokens(),
'X-RateLimit-Retry-After' => $limit->getRetryAfter()->getTimestamp(),
'Retry-After' => $limit->getRetryAfter()->getTimestamp() - time(),
]
);
}
// Add rate limit headers
$response = new JsonResponse(['data' => '...']);
$response->headers->set('X-RateLimit-Remaining', $limit->getRemainingTokens());
$response->headers->set('X-RateLimit-Limit', $limit->getLimit());
return $response;
}
}
<?php
// src/Service/ExportService.php
namespace App\Service;
use Symfony\Component\RateLimiter\RateLimiterFactory;
class ExportService
{
public function __construct(
private RateLimiterFactory $exportLimiter,
) {}
public function export(User $user): string
{
$limiter = $this->exportLimiter->create($user->getId());
$limit = $limiter->consume();
if (!$limit->isAccepted()) {
throw new TooManyRequestsException(
'Export limit reached. Please wait.',
$limit->getRetryAfter()
);
}
return $this->generateExport($user);
}
}
<?php
// src/Security/LoginRateLimiter.php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Security\Http\RateLimiter\AbstractRequestRateLimiter;
class LoginRateLimiter extends AbstractRequestRateLimiter
{
public function __construct(
private RateLimiterFactory $loginLimiter,
) {}
protected function getLimiters(Request $request): array
{
// Rate limit by IP + username combination
$username = $request->request->get('_username', '');
$ip = $request->getClientIp();
return [
$this->loginLimiter->create($ip),
$this->loginLimiter->create($username . $ip),
];
}
}
Configure in security:
# config/packages/security.yaml
security:
firewalls:
main:
form_login:
login_path: login
check_path: login
login_throttling:
limiter: login
<?php
// src/EventSubscriber/RateLimitSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\RateLimiter\RateLimiterFactory;
class RateLimitSubscriber implements EventSubscriberInterface
{
public function __construct(
private RateLimiterFactory $apiLimiter,
) {}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onRequest', 10],
];
}
public function onRequest(RequestEvent $event): void
{
$request = $event->getRequest();
// Only rate limit API routes
if (!str_starts_with($request->getPathInfo(), '/api/')) {
return;
}
$limiter = $this->apiLimiter->create($request->getClientIp());
$limit = $limiter->consume();
if (!$limit->isAccepted()) {
$event->setResponse(new JsonResponse(
['error' => 'Rate limit exceeded'],
Response::HTTP_TOO_MANY_REQUESTS,
['Retry-After' => $limit->getRetryAfter()->getTimestamp() - time()]
));
}
}
}
Wait for tokens instead of rejecting:
$limiter = $this->exportLimiter->create($user->getId());
// Will block until token is available (max 30 seconds)
$reservation = $limiter->reserve(1, 30);
// Wait for the reservation
$reservation->wait();
// Proceed with rate-limited operation
$this->generateExport($user);
<?php
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
class RateLimitTest extends TestCase
{
public function testRateLimitEnforced(): void
{
// Create limiter with in-memory storage for testing
$factory = new RateLimiterFactory([
'id' => 'test',
'policy' => 'fixed_window',
'limit' => 3,
'interval' => '1 minute',
], new InMemoryStorage());
$limiter = $factory->create('user_123');
// First 3 requests should succeed
for ($i = 0; $i < 3; $i++) {
$this->assertTrue($limiter->consume()->isAccepted());
}
// 4th request should fail
$this->assertFalse($limiter->consume()->isAccepted());
}
}
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.