Skip to content

refactor(app): central API client in src/lib/api.ts#8531

Merged
MarkusNeusinger merged 4 commits into
mainfrom
refactor/frontend-api-client
Jun 9, 2026
Merged

refactor(app): central API client in src/lib/api.ts#8531
MarkusNeusinger merged 4 commits into
mainfrom
refactor/frontend-api-client

Conversation

@MarkusNeusinger

Copy link
Copy Markdown
Owner

Summary

Part 5 of the frontend modernization roadmap.

  • src/lib/api.ts: apiGet/apiPost typed fetch wrappers that throw ApiError on non-2xx, plus an endpoints registry building every backend path from CONFIG.api.baseUrl (code, plots/filter, specs, insights, feedback, download, …)
  • fetchWithAuth (debug API: CF Access cookie + X-Admin-Token) moves out of DebugPage into lib/api as a named export
  • All 14 raw fetch() call sites across hooks/pages/components migrated; every non-2xx and abort code path preserved per call site (silent-skip stays silent, user-visible error strings unchanged)
  • useCodeFetch: isLoading is now a pending-request counter — a completing request no longer clears the loading state while another request is in flight. This resolves the deferred Copilot finding from chore(app): frontend tooling baseline — Prettier, import sorting, CI lint/type-check gates #8519.
  • Tests keep mocking globalThis.fetch, so they still assert the real constructed URLs end-to-end

Verification

yarn lint ✓ · yarn fm:check ✓ · yarn type-check (app + tests) ✓ · yarn test ✓ · yarn build ✓ · grep: no raw fetch( outside lib/api.ts

🤖 Generated with Claude Code

- apiGet/apiPost: typed fetch wrappers throwing ApiError on non-2xx;
  endpoints registry builds every backend path (code, plots/filter, specs,
  insights, feedback, download, ...) from CONFIG.api.baseUrl
- fetchWithAuth (debug API, CF Access cookie + X-Admin-Token) moves out of
  DebugPage into lib/api as a named export
- All 14 raw fetch call sites across hooks/pages/components migrated;
  non-2xx and abort code paths preserved exactly per call site
- useCodeFetch: isLoading is now a pending-request counter — a completing
  request no longer clears the loading state while another is in flight
  (deferred Copilot finding from #8519)

Part 5 of the frontend modernization roadmap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 9, 2026 23:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Refactors the frontend to centralize HTTP access through a typed API client (apiGet/apiPost, ApiError, and an endpoints registry) and migrates existing pages/hooks/components off raw fetch() calls, while preserving prior user-visible error/abort behavior.

Changes:

  • Migrates multiple call sites (pages, hooks, components) from fetch() + API_URL string-building to apiGet/apiPost + endpoints.
  • Moves the debug/admin authenticated fetch helper out of DebugPage into the shared API layer (fetchWithAuth).
  • Fixes overlapping-request loading state in useCodeFetch by tracking an in-flight counter instead of a boolean, and updates unit tests accordingly.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
app/src/pages/StatsPage.tsx Uses apiGet + endpoints for dashboard/visitors fetching; preserves prior error strings.
app/src/pages/SpecsListPage.tsx Uses apiGet for images fetch with abort handling; preserves prior non-2xx behavior.
app/src/pages/SpecPage.tsx Uses apiGet for spec loading and apiUrl(endpoints.download(...)) for downloads; preserves 404 messaging.
app/src/pages/MapPage.tsx Uses apiGet with AbortController and maps ApiError back to prior "HTTP <status>" banner text.
app/src/pages/DebugPage.tsx Replaces local adminFetch with shared fetchWithAuth for debug/admin endpoints.
app/src/hooks/usePlotOfTheDay.ts Switches plot-of-the-day fetch to apiGet.
app/src/hooks/useFilterFetch.ts Switches /plots/filter query building to endpoints.plotsFilter(...) + apiGet.
app/src/hooks/useFeaturedSpecs.ts Switches featured-specs image fetch to apiGet.
app/src/hooks/useCodeFetch.ts Refactors code fetching to apiGet + endpoints.code(...) and fixes loading state for concurrent requests.
app/src/hooks/useCodeFetch.test.ts Adjusts expectations for the new fetch wrapper call signature/URLs.
app/src/components/SpecTabs.tsx Switches cached tag-count fetch to apiGet while keeping silent-skip behavior.
app/src/components/RelatedSpecs.tsx Switches related-specs fetch to apiGet + endpoints.relatedSpecs(...).
app/src/components/PlotOfTheDay.tsx Switches plot-of-the-day fetch to apiGet.
app/src/components/Layout.tsx Switches initial metadata prefetching to apiGet with per-endpoint non-2xx compatibility behavior.
app/src/components/FeedbackWidget.tsx Switches feedback submission POSTs to apiPost + endpoints.feedback.

ThemeContext,
} from 'src/hooks/useLayoutContext';
import { useThemeMode } from 'src/hooks/useThemeMode';
import { ApiError, apiGet, endpoints } from 'src/lib/api';

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed and fixed — the root .gitignore's Python build-artifact rule (lib/) silently excluded app/src/lib/, so the module never made it into the commit. Added a scoped negation (!app/src/lib/) and committed the file; CI is re-running.

src/lib/api.ts was silently excluded by the root .gitignore's Python
build-artifact rule, so the API-client branch type-checked locally but
failed on CI with TS2307. Add a scoped negation and commit the file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.33333% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
app/src/hooks/useFeaturedSpecs.ts 0.00% 3 Missing ⚠️
app/src/hooks/usePlotOfTheDay.ts 0.00% 2 Missing ⚠️
app/src/pages/DebugPage.tsx 66.66% 1 Missing ⚠️
app/src/pages/MapPage.tsx 80.00% 1 Missing ⚠️
app/src/pages/SpecPage.tsx 80.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI review requested due to automatic review settings June 9, 2026 23:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Comment thread app/src/lib/api.ts
Comment on lines +60 to +66
const url = apiUrl(path);
const response = await fetch(url, {
...init,
method: 'POST',
headers: { 'Content-Type': 'application/json', ...(init?.headers as Record<string, string>) },
body: JSON.stringify(body),
});
Comment thread app/src/lib/api.ts
Comment on lines +82 to +86
const headers: Record<string, string> = { ...((init.headers as Record<string, string>) || {}) };
if (token) headers['X-Admin-Token'] = token;
if (init.body && !headers['Content-Type']) headers['Content-Type'] = 'application/json';
return fetch(url, { credentials: 'include', ...init, headers });
}
Comment on lines +105 to 109
images?: PlotImage[];
}>(endpoints.plotsFilter(params.toString()), { signal: abortController.signal });

if (abortController.signal.aborted) return;

@MarkusNeusinger MarkusNeusinger enabled auto-merge (squash) June 9, 2026 23:52
@MarkusNeusinger MarkusNeusinger merged commit 5585cb0 into main Jun 9, 2026
7 checks passed
@MarkusNeusinger MarkusNeusinger deleted the refactor/frontend-api-client branch June 9, 2026 23:54
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