Skip to content

refactor: use /rate_limit for token validation#159

Open
bluwy wants to merge 3 commits into
unjs:mainfrom
bluwy:rate-limit-endpoint
Open

refactor: use /rate_limit for token validation#159
bluwy wants to merge 3 commits into
unjs:mainfrom
bluwy:rate-limit-endpoint

Conversation

@bluwy

@bluwy bluwy commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator

This PR uses /rate_limit endpoint for token validation as the endpoint doesn't count against the limit compared to /meta.

This means we're free to refresh the status page without any impact, though perhaps we still want to put it behind auth to prevent abuse. It also means we can easily show the rate limit for other github apis, like graphql, in the future.

With this PR, there's updateStatusFromResponse and updateStatusFromRateLimitEndpoint to update the token status.

Summary by CodeRabbit

  • Documentation

    • Updated GitHub token lifecycle docs to reflect a switch to the rate-limit endpoint and the validation step costing 0 API requests.
  • Refactor

    • Reworked per-response and validation flows for more accurate rate-limit synchronization and clearer validation logging.
  • Tests

    • Updated test mocks and suites to align with the new validation approach and rate-limit responses.

@vercel

vercel Bot commented Mar 30, 2026

Copy link
Copy Markdown

@bluwy is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Changed GitHub token validation to call the /rate_limit endpoint (instead of /meta), renamed updateStatus()updateStatusFromResponse(), and added updateStatusFromRateLimitEndpoint(); updated callsites, tests, and documentation to reflect these changes.

Changes

Cohort / File(s) Summary
Documentation
./.agents/GH_API.md
Docs updated to show validate() probes /rate_limit, validation costs 0 API requests, and updateStatus(response) renamed to updateStatusFromResponse(response).
Token implementation
utils/github_token.ts
Introduced async updateStatusFromRateLimitEndpoint(); renamed updateStatus(res)updateStatusFromResponse(res); validate() now uses /rate_limit flow and logging adjusted.
Fetch / integration
utils/github.ts
Replaced token.updateStatus(res) with token.updateStatusFromResponse(res) after fetch completion.
Tests
test/github_token.test.ts
Added mockRateLimitResponse() and adjusted mockAppFetch to return /rate_limit JSON; renamed tests/group to updateStatusFromResponse; updated validation and revalidation tests to use /rate_limit responses and stricter unexpected-URL errors.

Sequence Diagram(s)

sequenceDiagram
    participant GHToken as GHToken.validate()
    participant GhFetch as ghFetch / utils/github
    participant GitHub as GitHub API (/rate_limit)

    GHToken->>GhFetch: GET /api.github.com/rate_limit
    GhFetch->>GitHub: HTTP GET /rate_limit
    GitHub-->>GhFetch: 200 + JSON rate_limit
    GhFetch-->>GHToken: Response
    GHToken->>GHToken: updateStatusFromRateLimitEndpoint() parses JSON and sets token.reset / remaining
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • pi0

Poem

🐰 I sniffed the logs and found a clue,
/rate_limit sings the status true.
I hopped, I parsed, I changed the name,
Now tokens dance without the old flame.
Cheers from a rabbit — light and new! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'refactor: use /rate_limit for token validation' directly and clearly describes the main technical change: switching from /meta to /rate_limit endpoint for token validation across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
utils/github_token.ts (1)

55-60: Use the repo's serverFetch() wrapper for this new HTTP call.

This new external request bypasses the standard Nitro fetch abstraction.

As per coding guidelines, Use serverFetch(url, opts) from nitro/app for internal and external HTTP requests (replaces $fetch).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@utils/github_token.ts` around lines 55 - 60, The rate-limit check in
utils/github_token.ts currently calls the global fetch directly (the call using
fetch("https://api.github.com/rate_limit", { headers: { Authorization: `token
${this.token}` } })) which bypasses the project's Nitro fetch wrapper; replace
that direct fetch invocation with the repo's serverFetch(url, opts) from
nitro/app, passing the same URL and headers (including Authorization: `token
${this.token}` and User-Agent) and await its result so the call uses the
standard serverFetch abstraction used across the project.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/GH_API.md:
- Around line 63-64: Update the two bullets to match current behavior: state
that ensureAllTokensValidated() bootstraps App tokens (JWT -> list installations
-> create installation tokens) before validating ghTokens, and note that ghFetch
now updates token/status inline after fetch() returns rather than relying on an
onResponse hook; reference ensureAllTokensValidated(), ghTokens, ghFetch, and
onResponse in the description so maintainers can find the related logic and
avoid reverting to the old execution order.

In `@test/github_token.test.ts`:
- Around line 22-39: Tests still use header-only mocks (mockResponse(200,
rateLimitHeaders(...))) but updateStatusFromRateLimitEndpoint() now calls
res.json() for 200 responses, causing parse errors; replace the success-path
header-only mocks with body-based mocks by using
mockRateLimitResponse(remaining, limit, resetEpoch?) instead of
mockResponse(...) in the suites that target ensureAllTokensValidated,
revalidateGHTokens, and acquireGHToken so the response includes the JSON body
the code expects.

In `@utils/github_token.ts`:
- Around line 61-67: The code currently sets this.valid = res.ok which treats
any non-2xx /rate_limit response (e.g., 403/429 secondary rate limits) as an
invalid token; update the logic in updateStatusFromResponse (or the method
handling the /rate_limit response) to only mark the token invalid when
res.status === 401, and otherwise preserve token validity while still reading
and applying rate info (parse res.json() when present and set this.remaining,
this.limit, this.reset from data.resources.core regardless of res.ok). Ensure
you do not swallow the reset bookkeeping on 403/429 and keep the existing
handling for 401 to mark this.valid = false.

---

Nitpick comments:
In `@utils/github_token.ts`:
- Around line 55-60: The rate-limit check in utils/github_token.ts currently
calls the global fetch directly (the call using
fetch("https://api.github.com/rate_limit", { headers: { Authorization: `token
${this.token}` } })) which bypasses the project's Nitro fetch wrapper; replace
that direct fetch invocation with the repo's serverFetch(url, opts) from
nitro/app, passing the same URL and headers (including Authorization: `token
${this.token}` and User-Agent) and await its result so the call uses the
standard serverFetch abstraction used across the project.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a296491b-2055-46ef-a549-0df7d1792761

📥 Commits

Reviewing files that changed from the base of the PR and between c9e3274 and 8750328.

📒 Files selected for processing (4)
  • .agents/GH_API.md
  • test/github_token.test.ts
  • utils/github.ts
  • utils/github_token.ts

Comment thread .agents/GH_API.md
Comment thread test/github_token.test.ts
Comment thread utils/github_token.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
utils/github_token.ts (1)

54-67: ⚠️ Potential issue | 🟠 Major

Still skip header-based quota updates on /rate_limit failures.

utils/github.ts:38-56 updates token state from every GitHub response before checking res.ok, but this helper only reads /rate_limit JSON on success. GitHub documents that rate-limit breaches can return 403/429, and that callers should use the rate-limit headers when possible to decide when to retry. Because updateStatusFromResponse(res) is never called here, a spent token can keep stale remaining/reset state and still look available. (docs.github.com)

🩹 Suggested fix
   async updateStatusFromRateLimitEndpoint() {
     const res = await fetch("https://api.github.com/rate_limit", {
       headers: {
         "User-Agent": "fetch",
         Authorization: `token ${this.token}`,
       },
     });
-    this.valid = res.status !== 401;
+    this.updateStatusFromResponse(res);
     if (res.ok) {
       const data = await res.json();
       this.remaining = data.resources.core.remaining;
       this.limit = data.resources.core.limit;
       this.reset = data.resources.core.reset * 1000;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@utils/github_token.ts` around lines 54 - 67, The
updateStatusFromRateLimitEndpoint method currently only parses JSON when res.ok
and therefore never calls updateStatusFromResponse(res) to update header-derived
quota info; modify updateStatusFromRateLimitEndpoint to always call
this.updateStatusFromResponse(res) immediately after receiving the Response
(before the res.ok check) so header-based remaining/limit/reset are applied even
on 403/429 responses, and then only parse JSON to overwrite values when res.ok
and JSON is present; reference updateStatusFromRateLimitEndpoint,
updateStatusFromResponse, this.token, and this.valid to locate and update the
logic.
🧹 Nitpick comments (1)
utils/github_token.ts (1)

55-60: Use serverFetch() for the new GitHub probe.

This PR adds another outbound HTTP path with raw fetch. Please route it through serverFetch() so token validation follows the repo's Nitro HTTP abstraction.

As per coding guidelines, "Use serverFetch(url, opts) from nitro/app for internal and external HTTP requests (replaces $fetch)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@utils/github_token.ts` around lines 55 - 60, Replace the raw fetch call in
utils/github_token.ts with the repo's Nitro HTTP abstraction by using
serverFetch instead: import serverFetch from 'nitro/app' (or the project's
canonical export) and call serverFetch("https://api.github.com/rate_limit", {
headers: { "User-Agent": "fetch", Authorization: `token ${this.token}` } }) in
place of fetch so the token validation path in the method that uses this.token
(token validation / rate limit probe) goes through the centralized HTTP layer;
preserve the existing await, response handling, and error paths as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@utils/github_token.ts`:
- Around line 54-67: The updateStatusFromRateLimitEndpoint method currently only
parses JSON when res.ok and therefore never calls updateStatusFromResponse(res)
to update header-derived quota info; modify updateStatusFromRateLimitEndpoint to
always call this.updateStatusFromResponse(res) immediately after receiving the
Response (before the res.ok check) so header-based remaining/limit/reset are
applied even on 403/429 responses, and then only parse JSON to overwrite values
when res.ok and JSON is present; reference updateStatusFromRateLimitEndpoint,
updateStatusFromResponse, this.token, and this.valid to locate and update the
logic.

---

Nitpick comments:
In `@utils/github_token.ts`:
- Around line 55-60: Replace the raw fetch call in utils/github_token.ts with
the repo's Nitro HTTP abstraction by using serverFetch instead: import
serverFetch from 'nitro/app' (or the project's canonical export) and call
serverFetch("https://api.github.com/rate_limit", { headers: { "User-Agent":
"fetch", Authorization: `token ${this.token}` } }) in place of fetch so the
token validation path in the method that uses this.token (token validation /
rate limit probe) goes through the centralized HTTP layer; preserve the existing
await, response handling, and error paths as-is.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3925d1d7-3bb3-48e3-bc52-c2de1a43202c

📥 Commits

Reviewing files that changed from the base of the PR and between 8750328 and 372eeef.

📒 Files selected for processing (2)
  • test/github_token.test.ts
  • utils/github_token.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/github_token.test.ts

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