Skip to content

fix(ratelimit-next): double-count in withRateLimit and broken default rule in middleware#10

Merged
sathergate merged 1 commit into
mainfrom
fix/ratelimit-next-bugs
Mar 30, 2026
Merged

fix(ratelimit-next): double-count in withRateLimit and broken default rule in middleware#10
sathergate merged 1 commit into
mainfrom
fix/ratelimit-next-bugs

Conversation

@sathergate
Copy link
Copy Markdown
Owner

Summary

  • withRateLimit double-count: withRateLimit was calling floodgate.check() twice per successful request — once inside rateLimit() and again to compute response headers. Since check runs the algorithm and mutates store counters, every allowed request consumed 2 tokens, effectively halving configured limits.
  • Middleware default rule always "default": createRateLimitMiddleware computed the fallback rule name with Object.keys({} as Record<string, unknown>)[0], which always returns undefined (empty object), always falling back to the literal string "default". If no rule named "default" was configured, floodgate would throw on every request.

Changes

  • floodgate.ts: Added readonly rules: FloodgateConfig["rules"] to the Floodgate interface and returns it from createFloodgate, so callers can inspect configured rules without casting.
  • next.ts: Inlined check + header logic inside withRateLimit (one call, one result, used for both the 429 and success header paths). Fixed middleware rule selection to use Object.keys(floodgate.rules)[0] and use the pre-computed ruleName variable consistently inside the handler.

Test plan

  • npx turbo run typecheck --filter=ratelimit-next passes
  • npm test — all 302 tests pass, 0 failures

🤖 Generated with Claude Code

… rule in middleware

withRateLimit called floodgate.check() twice per successful request — once
inside rateLimit() and again to compute headers — halving effective limits.
Fix: inline the check inside withRateLimit so one result covers both the
429 path and header injection.

createRateLimitMiddleware computed the default rule name from
Object.keys({}) which always returns [], so rule selection always fell
back to the literal string "default". If no rule named "default" existed,
floodgate would throw on every request. Fix: expose `rules` on the
Floodgate interface and use Object.keys(floodgate.rules)[0] instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sathergate sathergate merged commit 60d45a9 into main Mar 30, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant