Feature Requests
Planned

Centralize API calls: mandatory error handling, TanStack Query, and more (all TBD)

Nothing here is decided. This is a brainstorm surfaced while working the "API error codes: envelope, status fallback and guardrails" ticket — every idea below needs discussion and a real decision before any of it gets built. Do not treat any option as chosen.

Where this came from

That ticket's §3.4 mechanically routed ~50 Svelte components through resolveApiError(err), each wrapped in its own try { await someRoute(...) } catch (err) { error = resolveApiError(err); }. It works, but it's still up to every call site to remember the try/catch. Nothing stops a future component from skipping it and silently regressing to a raw/blank error, the exact problem the other ticket exists to close.

Today apps/web/src/lib/api/*.ts (catalog.ts, library.ts, etc.) are plain async () => request(...) functions with no shared calling convention beyond request() itself.

The core question

Should API calls go through a mandatory, centralized wrapper instead of bare route functions each component catches by hand?

Option A — a useApiCall-style factory per call

Instead of exporting getStatsOverview(domain) that can throw, export something that returns { data, error, loading } directly, replacing the $state ×3 + $effect + try/catch boilerplate seen in most components. apps/web/src/lib/components/stats/stats-resource.svelte.ts's statsResource() is close to this shape already, but scoped to /stats — this would generalize it app-wide.

Option B — keep bare route functions, but make the catch structural

Keep request()/route functions as-is, but provide a mandatory-feeling helper (createAsyncAction() or similar) every component calls explicitly, so nothing can bypass error resolution without visibly opting out.

TanStack Query — separate question, more mixed

@tanstack/svelte-query is already a dependency (CommentThread.svelte is the reference implementation — createInfiniteQuery, createMutation, queryClient.invalidateQueries, polling via refetchInterval). CLAUDE.md already says: "Reach for it, not another local-$state pattern, when a mutation needs to invalidate data shown in more than one component."

But adopting it isn't free even though it's zero new dependency — it's a paradigm shift (server state owned by the query, queryKey design, invalidation planning) for every screen migrated. Migrating ~50 call sites to it in one pass would be a second large mechanical migration, not an extension of the error-handling wrapper. If pursued, this should stay a separate, incremental migration (screen by screen, same pace as CommentThread.svelte set), not bundled into whatever the error-handling wrapper ends up being.

Refetch-on-window-focus (mentioned as a candidate use) is one of TanStack Query's built-ins, currently barely used in this codebase — worth deciding whether it's actually wanted broadly or just for specific screens.

Other ideas raised, also undecided

  • Automatic request cancellation (AbortController) when the initiating component unmounts, or when reactive params change before the previous response lands — would close a class of Svelte race-condition bugs ($effect re-firing before the prior fetch resolves) that likely already exist somewhere unnoticed.
  • A home for requestId. The API error envelope (from the other ticket) already carries requestId on ApiError, but nothing on the web side surfaces or uses it yet. A centralized wrapper would be the natural place to, e.g., tag it onto whatever error-reporting call the web app makes (Sentry/GlitchTip), so a user-visible failure can be traced back to a server log line.

Explicitly not decided

  • Whether to centralize at all vs. accept the current per-component discipline now that resolveApiError exists.
  • Option A vs. Option B vs. something else entirely.
  • Whether/how far to take TanStack Query adoption, and on what timeline relative to this.
  • Whether request cancellation and requestId surfacing belong in this effort or their own tickets.
  • Scope: touching all ~50 call sites again, or only new code going forward.

Depends on

"API error codes: envelope, status fallback and guardrails"ApiError/resolveApiError/requestId need to exist first (they do now, pending review).

0 Comments

Sign in to comment

No comments yet. Be the first to share your thoughts!