diff --git a/eslint-plugins/healthlog/api-fetch-required.js b/eslint-plugins/healthlog/api-fetch-required.js index 028bb33ce..67da72ca5 100644 --- a/eslint-plugins/healthlog/api-fetch-required.js +++ b/eslint-plugins/healthlog/api-fetch-required.js @@ -43,8 +43,16 @@ const EXEMPT_FILES = ["src/lib/api/api-fetch.ts"]; // server-side lib code never self-calls `/api/` routes, and the // safe-fetch rule already covers its outbound calls. `src/hooks/` is // client-only by construction (v1.16.4) — its `/api/` reads route -// through the wrapper like every component. -const ENFORCED_ROOTS = ["src/components/", "src/app/", "src/hooks/"]; +// through the wrapper like every component. `src/lib/queries/` is the +// one `src/lib/` exception: it holds client-only TanStack-Query hooks +// (every file is `"use client"`), so its `/api/` reads route through the +// wrapper too. +const ENFORCED_ROOTS = [ + "src/components/", + "src/app/", + "src/hooks/", + "src/lib/queries/", +]; function toPosix(filename) { return filename.replace(/\\/g, "/"); diff --git a/src/lib/queries/use-achievements-query.ts b/src/lib/queries/use-achievements-query.ts index f1d42efec..88f7f7574 100644 --- a/src/lib/queries/use-achievements-query.ts +++ b/src/lib/queries/use-achievements-query.ts @@ -3,6 +3,7 @@ import { useQuery, type UseQueryResult } from "@tanstack/react-query"; import { useAuth } from "@/hooks/use-auth"; +import { apiGet } from "@/lib/api/api-fetch"; import { queryKeys } from "@/lib/query-keys"; import type { AchievementMetrics, @@ -65,13 +66,8 @@ export interface UseAchievementsQueryOptions { refetchInterval?: number | false; } -async function fetchAchievements(): Promise { - const res = await fetch("/api/gamification/achievements"); - if (!res.ok) { - throw new Error(`Failed to load achievements (${res.status})`); - } - const json = (await res.json()) as { data: AchievementsPayload }; - return json.data; +function fetchAchievements(): Promise { + return apiGet("/api/gamification/achievements"); } /** diff --git a/src/lib/queries/use-analytics-query.ts b/src/lib/queries/use-analytics-query.ts index 2fb964a2b..52282a445 100644 --- a/src/lib/queries/use-analytics-query.ts +++ b/src/lib/queries/use-analytics-query.ts @@ -3,6 +3,7 @@ import { useQuery, type UseQueryResult } from "@tanstack/react-query"; import { useAuth } from "@/hooks/use-auth"; +import { apiGet } from "@/lib/api/api-fetch"; import { queryKeys } from "@/lib/query-keys"; import { DASHBOARD_REFETCH_INTERVAL_MS } from "@/lib/queries/refetch-interval"; import type { DataSummary } from "@/lib/analytics/trends"; @@ -114,18 +115,10 @@ export interface UseAnalyticsQueryOptions { enabled?: boolean; } -async function fetchAnalytics( - slice: AnalyticsSlice, -): Promise { - const res = - slice === "summaries" - ? await fetch("/api/analytics?slice=summaries") - : await fetch("/api/analytics"); - if (!res.ok) { - throw new Error(`Failed to load analytics (${res.status})`); - } - const json = (await res.json()) as { data: AnalyticsRawPayload }; - return json.data; +function fetchAnalytics(slice: AnalyticsSlice): Promise { + return apiGet( + slice === "summaries" ? "/api/analytics?slice=summaries" : "/api/analytics", + ); } /**