Skip to content

feat(rate-limit): make tier quotas configurable and return standard rate-limit headers - #79

Open
produckq-boop wants to merge 1 commit into
octraban:mainfrom
produckq-boop:feat/configurable-rate-limit-headers-50
Open

produckq-boop wants to merge 1 commit into
octraban:mainfrom
produckq-boop:feat/configurable-rate-limit-headers-50

Conversation

@produckq-boop

Copy link
Copy Markdown

Closes #50

Summary

Makes token-bucket rate-limit tiers configurable via environment variables while keeping existing defaults, standardizes HTTP rate-limit response headers across both Redis and in-memory paths, and documents the tier quotas, headers, and per-key override flow.

Previously, TIER_LIMITS and the 60-second refill calculation in src/middleware/tokenBucket.ts were hardcoded, responses only set legacy X-RateLimit-* headers, and tieredRateLimit in src/middleware/rateLimit.ts skipped checkTokenBucket whenever REDIS_URL was unset (even though checkTokenBucket already has an in-memory token-bucket fallback), which also caused DevApiKey.rateLimitOverride to be ignored in non-Redis environments.

Changes

  • src/middleware/tokenBucket.ts:
    • Add DEFAULT_TIER_LIMITS, getTierLimits(tier, env?), and resolveTierLimits(env?) supporting RATE_LIMIT_TIER_<TIER>_LIMIT and RATE_LIMIT_TIER_<TIER>_WINDOW_MS (plus optional RATE_LIMIT_TIER_<TIER>_BURST) for unauthenticated, free, developer, pro, and enterprise, falling back to current defaults when unset or invalid.
    • Compute token refill rate from the configured windowMs (capacity / (windowMs / 1000)) instead of a hardcoded 60-second window, while preserving TIER_LIMITS and perMinute for backward compatibility.
    • Add applyRateLimitHeaders() and update tokenBucketMiddleware() (with a default req.apiKey / IP tier resolver) to emit standard RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (delta seconds) alongside existing X-RateLimit-* headers, plus Retry-After on 429 responses.
  • src/middleware/rateLimit.ts & src/index.ts:
    • Route tieredRateLimit through checkTokenBucket regardless of whether Redis is configured so tier quotas, endpoint multipliers, and req.apiKey?.rateLimitOverride behave consistently across Redis and in-memory execution, while keeping runLocalRateLimit as an error fallback that also emits RateLimit-* and Retry-After.
    • Expose RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, Retry-After, and X-RateLimit-* in CORS exposedHeaders.
  • src/middleware/apiKeyAuth.ts:
    • Normalize stored API key tier strings (normalizeRateLimitTier, defaulting unknown values to 'free') and ignore non-positive rateLimitOverride values.
  • .env.example, README.md, package.json:
    • Document RATE_LIMIT_TIER_* variables in .env.example and add a Rate Limiting section in README.md covering tiers, env vars, endpoint multipliers, DevApiKey.rateLimitOverride / cache invalidation, and response headers.
    • Consolidate the duplicate "test" key in package.json and include tests/api-key-auth.test.ts, tests/rate-limit.test.ts, and tests/token-bucket.test.ts in npm test.
  • tests/rate-limit.test.ts & tests/token-bucket.test.ts:
    • Expand tests/rate-limit.test.ts and add tests/token-bucket.test.ts covering within-limit remaining quota headers, 429 responses with Retry-After and RateLimit-* headers, custom env limits/windows and invalid-env fallbacks, window refill behavior, apiKeyAuth rateLimitOverride integration, endpoint multipliers, and Redis-to-memory fallback.

Verification

  • npm run lint (0 errors)
  • npm run build & npm run build -- --noEmit & npm run typecheck:scripts
  • npm run validate:prisma & npm run check:mock-gating
  • npm test (14 test files, 268 tests passing)

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.

Make rate-limit tiers configurable and return standard rate-limit headers

1 participant