Expose rate-limit headers consistently and document the contract
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 30/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- nodejs, typescript
- Domain
- api, backend, documentation
Research direction
Start with the global guard registered at src/app.module.ts:311 and the enforcement logic in src/rate-limiting/rate-limiter.service.ts, then check which headers each path sets today. Resolve the conflict between the README's 100 req/min statement (line 155) and the per-tier variables (lines 239-241) before changing code. Done means headers appear on successful and 429 responses, Retry-After is accurate, docs and CHANGELOG.md are updated, and tests cover both paths.
Written by the indexing model from the issue text.
Description
Problem
Rate limiting is applied globally via DistributedRateLimitGuard (src/app.module.ts:311), but the response contract is not documented and its consistency across paths is unverified.
The README states "Rate limiting (100 req/min per IP)" (line 155) as a security feature, and separately documents tiered configuration at lines 239-241 (RATE_LIMIT_FREE_PER_MINUTE, RATE_LIMIT_PAID_PER_MINUTE, RATE_LIMIT_ENTERPRISE_PER_MINUTE). Those two statements already disagree — 100/min globally versus per-tier.
src/rate-limiting/rate-limiter.service.ts is the enforcement point and rate-limiting.controller.ts exposes administrative views, but there is no documented guarantee about which headers are set (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After), whether all responses carry them, or what a client should do with them.
There is also an unresolved correctness question from the in-memory fallback: when Redis is unavailable, limits become per-process, so a client behind N instances effectively gets N× the allowance, and nothing communicates that.
Why it matters
Clients cannot implement correct retry or backoff behaviour without knowing the contract. Well-behaved SDKs throttle themselves using X-RateLimit-Remaining and honour Retry-After; without documented headers they instead react to 429s, which is worse for everyone. The README's own inconsistency means an integrator cannot tell what to expect.
Suggested Direction
- Define the header contract and write it down: which headers, on which responses (including successful ones, not just 429), and what
Retry-Aftercontains. The IETFRateLimitheader draft standardises this — consider adopting it, possibly alongside the legacyX-names for compatibility. - Set the headers on every rate-limited response, not only on rejection. Clients need the remaining budget before they hit the wall.
- Resolve the README contradiction — either the global default is 100/min and tiers override it, or something else. State which.
- Document the multi-instance caveat. When the in-memory fallback is active, limits are per-process; either log a warning at startup, expose the active mode via
GET /api/v1/meta(see the meta-endpoint issue), or document it indocs/. - Add a
RateLimit-ResetorRetry-Aftervalue on 429 that is accurate, so a well-behaved client can retry precisely rather than guessing.
Constraints
- Headers are set in the guard, so all of this must work across every route the global guard covers — including WebSocket upgrades, which behave differently.
- Do not leak internal configuration through headers (no pool sizes, no tier internals beyond the effective limit).
- Keep the existing header names if any clients already rely on them; adding is safer than renaming.
- Changing default limits is a behaviour change — note it in
CHANGELOG.md.
Acceptance Criteria
- The header contract is documented in
docs/and in the README. - Rate-limit headers are present on successful responses from rate-limited routes.
- A 429 response includes an accurate
Retry-After. - The README's rate-limit statements are consistent.
- The multi-instance fallback behaviour is documented and discoverable.
- Tests assert header presence and values on both success and 429 paths.
- Dominant language
- TypeScript
- Stars
- 7
- Forks
- 55
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 5
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from SourceXXL/alian_structure-api
-
good first issue help wanted security
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SourceXXL/alian_structure-api#266 ·
-
Parallelize the cache-warming loop in `cache-warming.service.ts` — warmers run strictly sequentiallyPossibly taken @syntaxsuuvam-spec claimed this 4 days ago. Opengood first issue performance
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SourceXXL/alian_structure-api#240 ·
-
documentation help wanted intermediate refactor
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
SourceXXL/alian_structure-api#215 ·
-
good first issue refactor tooling
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
SourceXXL/alian_structure-api#199 ·
-
help wanted intermediate refactor
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
SourceXXL/alian_structure-api#196 ·
All issues in SourceXXL/alian_structure-api
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
wormhole-foundation/wormhole#5062 ·
Maintainers usually reply within 1 day
-
internal os specific
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
frostney/GocciaScript#1688 ·
Maintainers usually reply within 1 day
-
integration:hermes
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
vectorize-io/hindsight#5592 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
vercel/streamdown#673 · 1 reaction ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
apache/rocketmq-dashboard#6712 ·
Maintainers usually reply within 3 days