Skip to content

feat: send X-Spoo-Client attribution header on backend calls - #23

Merged
Zingzy merged 1 commit into
mainfrom
feat/client-header
Jul 22, 2026
Merged

feat: send X-Spoo-Client attribution header on backend calls#23
Zingzy merged 1 commit into
mainfrom
feat/client-header

Conversation

@Zingzy

@Zingzy Zingzy commented Jul 22, 2026

Copy link
Copy Markdown
Member

Every request this app makes to the spoo.me backend now carries an X-Spoo-Client header so the API can attribute traffic by client.

Values sent:

  • dashboard when the call originates from the signed-in app (routes under /dashboard and /onboarding)
  • landing for the public surface (home shorten box, public stats and preview pages, report and contact forms, error pages) and for server-side fetches, which only happen from public pages

The header is stamped in one place: a new apiFetch wrapper in lib/api/client.ts that infers the surface from the current route. authedFetch and the refresh call go through it, and the handful of raw fetch callsites in lib/api and components were switched over. Third-party fetches (favicon upstream, static geo assets, PostHog relay) are untouched and never see the header.

No CORS coordination needed: every browser call rides the same-origin rewrite proxy, so no preflight is involved and this can merge independently of the backend change that reads the header. Until that lands the backend simply ignores it.

Includes unit tests for the surface inference and header injection.

Every request to the spoo.me backend now carries X-Spoo-Client so the
API can attribute traffic by client. One wrapper (apiFetch) stamps the
header; the surface is inferred from the route: /dashboard and
/onboarding send dashboard, everything else sends landing.
Third-party fetches (favicon upstream, geo assets) stay untouched.
Copilot AI review requested due to automatic review settings July 22, 2026 13:12

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Zingzy, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: c33eb93c-969c-4ba4-b7b5-02e49ffaa3a1

📥 Commits

Reviewing files that changed from the base of the PR and between 246aed8 and bf82bb4.

📒 Files selected for processing (10)
  • components/errors/alias-claim.tsx
  • components/errors/not-found-body.tsx
  • components/sections/instant-shortener.tsx
  • lib/api/auth.ts
  • lib/api/client.test.ts
  • lib/api/client.ts
  • lib/api/links.ts
  • lib/api/public-preview.ts
  • lib/api/public-stats.ts
  • lib/api/reports.ts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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

@Zingzy Zingzy left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Mergeable, nothing blocking. One cross-repo observation worth a conscious call, inline below.

Coverage traced rather than assumed:

  • Every remaining raw fetch in the repo is deliberately untagged: apiFetch's own inner call, the country-map static asset, and the favicon proxy upstream (third-party host). lib/api/server.ts only builds the base URL; the server-side public page fetches go through the converted getPublicStats/getPublicPreview. Coverage is complete.
  • authedFetch routes through apiFetch on both the original request and the post-refresh retry, and the refresh call itself is tagged.
  • headers.set stamps last, so a caller-provided X-Spoo-Client can never slip through. Right default.
  • The /dashboardish test case pins the route-boundary regex, which is exactly the case that would have silently misclassified.

Ran the new apiFetch tests locally (5 pass); repo checks are green.

Comment thread components/sections/instant-shortener.tsx
@Zingzy
Zingzy merged commit 79b09e0 into main Jul 22, 2026
2 checks passed
@Zingzy
Zingzy deleted the feat/client-header branch August 16, 2026 12:37
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.

2 participants