Skip to content

fix(api): restore the ?domain= dispatch in the /api/icon Vercel wrapper - #297

Merged
forcingfx merged 1 commit into
mainfrom
fix/icon-wrapper-dispatch
Sep 9, 2026
Merged

forcingfx merged 1 commit into
mainfrom
fix/icon-wrapper-dispatch

Conversation

@forcingfx

Copy link
Copy Markdown
Owner

What

Since #260 (2026-08-05) production answered every /api/icon?domain=<host> request with 400 "Missing url". That is the client's primary favicon path (feed-favicon.tsx), so every client fell back to proxying raw site URLs, and the generic proxy relayed whatever came back, including 1.3 MB of NYT homepage HTML per attempt, as an "icon" with max-age=0, never CDN-cached.

Two Vercel alerts trace to this: the 5xx spike on /api/icon (2026-09-08, upstream 502s from the fallback path) and Fast Origin Transfer at 100% of the free 10 GB (2026-09-04).

Verified live before the fix:

/api/icon?domain=nytimes.com          -> 400, 21 B
/api/icon?url=https://www.nytimes.com -> 200, 1,372,581 B, text/html, max-age=0, x-vercel-cache: MISS

Why

#260 rewrote api/icon.ts as a thin call to handleProxyRequest and dropped the query-shape dispatch that server.ts and vite.config.js both carry. The wrapper contract test only checks that api/*.ts import from src/, not what they route, and there was no smoke test for the endpoint.

Fix

The wrapper dispatches ?domain= to handleFaviconRequest and everything else to the image proxy, the same branch as the other two entry points (three-entry-point rule).

Prevention

  • tests/api/icon-wrapper.test.ts: runs the wrapper's GET with only fetch mocked and asserts both shapes (red before: 400).
  • tests/smoke/icon.test.ts: asserts the deployed function serves ?domain= as a cacheable image.

Verification

  • npx vitest run: full suite green. npx tsc --noEmit: clean. scripts/build-api.js bundles the new wrapper.
  • After merge: SMOKE_TESTS=1 npx vitest run tests/smoke/icon.test.ts against production.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GQoLHbPfaTCHvPuaoqGZeJ

What: since PR #260 (2026-08-05) production answered every
/api/icon?domain=<host> request with 400 "Missing url". That is the
client's primary favicon path (feed-favicon.tsx), so the client fell back
to proxying raw site URLs; the generic proxy relayed whatever came back —
1.3 MB of NYT homepage HTML per attempt — as an "icon" with
max-age=0, so the CDN never cached it. Vercel alerted on a 5xx spike on
/api/icon (2026-09-08, upstream 502s from the fallback) and on Fast Origin
Transfer hitting 100% of the free 10 GB (2026-09-04).

Why: #260 rewrote api/icon.ts as a thin call to handleProxyRequest and
dropped the query-shape dispatch that server.ts and vite.config.js carry.
The wrapper contract test checks that api/*.ts import from src/, not what
they route; nothing exercised the wrapper's behaviour and no smoke test
hit the live endpoint.

Fix: the wrapper dispatches ?domain= to handleFaviconRequest, everything
else to the image proxy — the same branch as the other two entry points.

Prevention: tests/api/icon-wrapper.test.ts runs the wrapper's GET with only
fetch mocked and asserts both shapes; tests/smoke/icon.test.ts asserts
the deployed function serves ?domain= as a cacheable image.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQoLHbPfaTCHvPuaoqGZeJ
@vercel

vercel Bot commented Sep 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
feedzero Ready Ready Preview Sep 9, 2026 8:30pm UTC

Request Review

@forcingfx
forcingfx enabled auto-merge (squash) September 9, 2026 20:30
@forcingfx
forcingfx merged commit 020dbe3 into main Sep 9, 2026
17 checks passed
forcingfx added a commit that referenced this pull request Sep 9, 2026
…ction (#298)

The test added in #297 failed on its first live run for two reasons that
the unit run could not show: example.com has no favicon (its ?domain=
answer is an empty text/plain 200 and its /favicon.ico a 404), and the
default happy-dom environment blocks the cross-origin fetch outright.
Fixtures are now our own domain and the file runs under node like every
other smoke test. Verified green against my.feedzero.app before commit.


Claude-Session: https://claude.ai/code/session_01GQoLHbPfaTCHvPuaoqGZeJ

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview cee51094 Deployed Sep 9, 2026 by vercel[bot]
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