refactor(graphql): improve graphql rate limiting (#1162)

This commit is contained in:
Kyrch
2026-04-10 00:32:43 -03:00
committed by GitHub
parent cd11c5fa3b
commit 2f1a55a433
4 changed files with 45 additions and 45 deletions
+1 -1
View File
@@ -175,7 +175,7 @@ FORTIFY_URL=http://localhost
# graphql
GRAPHQL_URL=
GRAPHQL_PATH=/graphql
GRAPHQL_RATE_LIMIT=90
GRAPHQL_RATE_LIMIT=135
GRAPHIQL_ENABLED=true
# hashids
+1 -1
View File
@@ -173,7 +173,7 @@ FORTIFY_URL=http://localhost
# graphql
GRAPHQL_URL=
GRAPHQL_PATH=/graphql
GRAPHQL_RATE_LIMIT=90
GRAPHQL_RATE_LIMIT=135
GRAPHIQL_ENABLED=true
# hashids
@@ -41,49 +41,49 @@ class RateLimitPerQuery
$ip = $forwardedIp;
}
if ($query) {
$rootFields = 0;
try {
$ast = Parser::parse($query);
foreach ($ast->definitions as $definition) {
if ($definition instanceof OperationDefinitionNode) {
$rootFields += count($definition->selectionSet->selections ?? []);
}
}
} catch (JsonException|SyntaxError) {
// Do nothing
}
$hits = max(1, $rootFields);
$key = sprintf('graphql:%s', Auth::id() ?? $ip);
foreach (range(1, $hits) as $_) {
if (RateLimiter::tooManyAttempts($key, $limit)) {
$retryAfter = RateLimiter::availableIn($key);
return new JsonResponse([
'message' => 'Too Many Attempts.',
], 429, [
'Retry-After' => $retryAfter,
'X-RateLimit-Limit' => $limit,
'X-RateLimit-Remaining' => RateLimiter::remaining($key, $limit),
'X-RateLimit-Reset' => now()->addSeconds($retryAfter)->getTimestampMs(),
]);
}
RateLimiter::hit($key);
}
return $next($request)
->withHeaders([
'X-RateLimit-Limit' => $limit,
'X-RateLimit-Remaining' => RateLimiter::remaining($key, $limit),
]);
if (! $query) {
return $next($request);
}
return $next($request);
$rootFields = 0;
try {
$ast = Parser::parse($query);
foreach ($ast->definitions as $definition) {
if ($definition instanceof OperationDefinitionNode) {
$rootFields += count($definition->selectionSet->selections ?? []);
}
}
} catch (JsonException|SyntaxError) {
// Do nothing
}
$hits = max(1, $rootFields);
$key = sprintf('graphql:%s', Auth::id() ?? $ip);
$current = RateLimiter::attempts($key);
if (($current + $hits) > $limit) {
$retryAfter = RateLimiter::availableIn($key);
return new JsonResponse([
'message' => 'Too Many Attempts.',
], 429, [
'Retry-After' => $retryAfter,
'X-RateLimit-Limit' => $limit,
'X-RateLimit-Remaining' => RateLimiter::remaining($key, $limit),
'X-RateLimit-Reset' => now()->addSeconds($retryAfter)->getTimestampMs(),
]);
}
RateLimiter::increment($key, amount: $hits);
return $next($request)
->withHeaders([
'X-RateLimit-Limit' => $limit,
'X-RateLimit-Remaining' => RateLimiter::remaining($key, $limit),
]);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ use App\GraphQL\Schema\Types\Base\PaginationInfoType;
return [
'domain' => env('GRAPHQL_URL', env('APP_URL')),
'rate_limit' => (int) env('GRAPHQL_RATE_LIMIT', 90),
'rate_limit' => (int) env('GRAPHQL_RATE_LIMIT', 135),
'route' => [
// The prefix for routes; do NOT use a leading slash!