Skip to content

Phase:2- Refactoring#91

Merged
Ayush8923 merged 7 commits intomainfrom
feat/refactor-routes-phase-2
Mar 26, 2026
Merged

Phase:2- Refactoring#91
Ayush8923 merged 7 commits intomainfrom
feat/refactor-routes-phase-2

Conversation

@Ayush8923
Copy link
Collaborator

@Ayush8923 Ayush8923 commented Mar 24, 2026

Isssue: #71

Summary:

  • Use the common function to apiClient function in the route.ts file.
  • Fix the formatting also.
  • Adding the caching mechanism in the AuthContext.ts file, and fix the hydration issues.

Summary by CodeRabbit

  • Refactor

    • Centralized outbound API proxying across routes for more consistent and reliable backend requests
    • Standardized error response formatting across API endpoints
  • Bug Fixes

    • Fixed file upload header/content-type handling to avoid multipart boundary issues
  • UX / Data Loading

    • Deferred config loading until authentication state is hydrated to prevent premature or failed fetches

@coderabbitai
Copy link

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

Refactors many API route handlers to use a centralized apiClient/apiFetch instead of manually constructing backend URLs and forwarding X-API-KEY; updates useConfigs to use useAuth/isHydrated and switches some fetches to apiFetch; fixes Content-Type handling for FormData in apiClient; minor error-response formatting changes.

Changes

Cohort / File(s) Summary
Credential Routes
app/api/credentials/[provider]/route.ts, app/api/credentials/route.ts
Reformatted error NextResponse.json calls (multi-line). No change to payload shapes, status codes, or control flow.
TTS Evaluation Routes
app/api/evaluations/tts/datasets/route.ts, app/api/evaluations/tts/datasets/[dataset_id]/route.ts, app/api/evaluations/tts/results/[result_id]/route.ts, app/api/evaluations/tts/runs/route.ts, app/api/evaluations/tts/runs/[run_id]/route.ts
Replaced direct backend fetch + process.env.BACKEND_URL + X-API-KEY header plumbing with apiClient(request, endpoint, options). Removed some explicit 401 early-returns for missing API keys; retained CSV download and response status propagation.
STT & Languages Routes
app/api/evaluations/stt/files/route.ts, app/api/languages/route.ts
Delegated outbound requests to apiClient; simplified response handling to return { data, status } from helper. Minor error-message/formatting normalization.
API Client & Fetch Helpers
app/lib/apiClient.ts
Conditionally set Content-Type: application/json only when body is not FormData to avoid overriding multipart boundaries.
Auth Context & Hooks
app/lib/context/AuthContext.tsx, app/hooks/useConfigs.ts
Added isHydrated to auth context; updated useConfigs to use useAuth()/activeKey?.key, defer fetches until hydrated, use apiFetch for typed calls, and refactor caching/loading control flow. No exported signatures changed (minor formatting adjustments only).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant NextRoute as "Next API Route"
  participant ApiClient as "apiClient / apiFetch"
  participant Backend as "Backend API"

  Client->>NextRoute: HTTP request (includes cookies/headers)
  NextRoute->>ApiClient: apiClient(request, endpoint, options)
  ApiClient->>Backend: Forwarded request (backend URL, headers, body)
  Backend-->>ApiClient: { status, data } / signed URL or CSV
  ApiClient-->>NextRoute: { status, data }
  NextRoute-->>Client: NextResponse.json(data, { status })
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #58: Modifies the same TTS evaluation route handlers and replaces direct backend fetches with apiClient—direct overlap with route refactors here.
  • PR #89: Centralizes API key/auth context and switches callers to apiClient/apiFetch, touching useAuth/context and many routes updated in this PR.
  • PR #76: Adjusts credential route handlers and related formatting/error-response behavior overlapping with credential-file edits here.

Suggested reviewers

  • Prajna1999
  • vprashrex

Poem

🐰
I hustled through routes both near and far,
Swapped fetch for apiClient, kept each header scar,
FormData now safe, no boundary lost in flight,
Auth wakes hydrated before we call the night,
A rabbit applauds — tidy code, done right! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title "Phase:2- Refactoring" is vague and does not convey meaningful information about the specific changes in the changeset. Provide a more descriptive title that summarizes the main changes, such as 'Refactor API routes to use centralized apiClient helper' or 'Consolidate route handlers with apiClient and fix hydration issues'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/refactor-routes-phase-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Ayush8923 Ayush8923 self-assigned this Mar 24, 2026
@Ayush8923 Ayush8923 requested a review from vprashrex March 25, 2026 12:03
@Ayush8923 Ayush8923 added duplicate This issue or pull request already exists ready-for-review and removed duplicate This issue or pull request already exists labels Mar 25, 2026
Base automatically changed from feat/refactor-routes-phase-1 to main March 26, 2026 14:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/evaluations/tts/datasets/`[dataset_id]/route.ts:
- Around line 27-47: The handler currently dereferences signed_url even when the
upstream fetch failed; update the fetch_content branch to short-circuit on the
upstream response status before reading data.data.signed_url (or
data.signed_url): if the upstream response is non-2xx, return a NextResponse
with the upstream status and error/body instead of proceeding to check
signed_url; only after confirming a successful upstream response should you
attempt to read signed_url and fetch the CSV (refer to variables fetchContent,
data, signed_url, and NextResponse.json to implement this).
- Around line 66-71: The delete handler currently always calls NextResponse.json
even when apiClient returns status 204 and data is null; change the logic in the
route (the block that calls
apiClient(`/api/v1/evaluations/tts/datasets/${dataset_id}`, { method: "DELETE"
})) to check if status === 204 and, if so, return a bare NextResponse with no
JSON body (e.g., new NextResponse(null, { status })) instead of
NextResponse.json; otherwise keep returning NextResponse.json(data, { status }).

In `@app/api/evaluations/tts/runs/`[run_id]/route.ts:
- Around line 12-14: The endpoint string concatenation uses the raw run_id which
can contain reserved characters; change the construction of endpoint in route.ts
to use encodeURIComponent(run_id) (e.g., encodeURIComponent(run_id)) when
building the path so the upstream URL cannot be rewritten by dot-segments or
reserved chars; apply the same encoding safeguard to other dynamic ID routes in
this PR that build URLs from params.

In `@app/hooks/useConfigs.ts`:
- Around line 103-164: Fast-path cache and pendingFetch are not scoped to the
request inputs (apiKey, pageSize), so cached inMemory/localStorage entries and
configState.pendingFetch can be reused across different apiKey/pageSize
combinations; namespace the cache and pending promise by those inputs or clear
them on change. Concretely: change configState.inMemoryCache and the object
persisted by loadCache() to include apiKey and pageSize (e.g., { apiKey,
pageSize, partialFetch, configs, ... }) and only reuse when cached.apiKey ===
current apiKey && cached.pageSize === current pageSize; similarly attach the
same key to configState.pendingFetch (or store pendingFetch map keyed by
`${apiKey}|${pageSize}`) and ensure you clear/replace pendingFetch and
inMemoryCache when apiKey or pageSize changes; apply the same scoping for the
localStorage usage and for scheduleBackgroundValidation usage (see the
fast-paths around configState.inMemoryCache and loadCache() and the subsequent
block at ~169-205).
- Around line 92-93: The memoized callbacks in useConfigs.ts (fetchConfigs,
loadVersionsForConfig, loadSingleVersion, loadMoreConfigs) close over auth and
can capture a null apiKey; update their dependency arrays to include apiKey (or
activeKey where appropriate) so they re-create when auth hydrates: set
fetchConfigs deps to [apiKey, pageSize], loadVersionsForConfig to [apiKey],
loadSingleVersion to [apiKey, configs], and loadMoreConfigs to [apiKey, configs,
pageSize] (or substitute activeKey consistently) to prevent stale closures.
- Around line 97-100: useConfigs is surfacing "No API key found" prematurely
because useAuth() initializes activeKey=null and hydration from localStorage
happens asynchronously; add an isHydrated (or isReady) boolean to AuthContext
that flips true after the mount effect finishes reading localStorage, expose it
from useAuth(), then update fetchConfigs (and its useEffect) to wait for
isHydrated before checking apiKey and setting the error—i.e., only run the
!apiKey branch when isHydrated is true (or when apiKey is explicitly null after
hydration) so the initial null does not trigger a false error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 00940a48-7ae8-4498-9e52-c1e7efd30849

📥 Commits

Reviewing files that changed from the base of the PR and between 80d0ae6 and 8a63136.

📒 Files selected for processing (13)
  • app/api/credentials/[provider]/route.ts
  • app/api/credentials/route.ts
  • app/api/evaluations/stt/files/route.ts
  • app/api/evaluations/tts/datasets/[dataset_id]/route.ts
  • app/api/evaluations/tts/datasets/route.ts
  • app/api/evaluations/tts/results/[result_id]/route.ts
  • app/api/evaluations/tts/runs/[run_id]/route.ts
  • app/api/evaluations/tts/runs/route.ts
  • app/api/languages/route.ts
  • app/hooks/useConfigs.ts
  • app/lib/apiClient.ts
  • app/speech-to-text/page.tsx
  • app/text-to-speech/page.tsx

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/lib/context/AuthContext.tsx (1)

32-36: ⚠️ Potential issue | 🟡 Minor

Validate parsed localStorage payload before setApiKeys.

JSON.parse can return non-array JSON, which would later break array operations on apiKeys. Guard the shape before updating state.

Suggested fix
   useEffect(() => {
     try {
       const stored = localStorage.getItem(STORAGE_KEY);
       // eslint-disable-next-line react-hooks/set-state-in-effect
-      if (stored) setApiKeys(JSON.parse(stored));
+      if (stored) {
+        const parsed = JSON.parse(stored);
+        if (Array.isArray(parsed)) {
+          setApiKeys(parsed as APIKey[]);
+        } else {
+          localStorage.removeItem(STORAGE_KEY);
+        }
+      }
     } catch {
       /* ignore malformed data */
     }
     setIsHydrated(true);
   }, []);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/context/AuthContext.tsx` around lines 32 - 36, The code reads
localStorage.getItem(STORAGE_KEY) and JSON.parse(...) then unconditionally calls
setApiKeys, but JSON.parse may return non-array values; update the try block in
AuthContext (where STORAGE_KEY and setApiKeys are used) to parse the stored
value, verify it's an array (and optionally validate expected item shape), and
only call setApiKeys(parsed) when Array.isArray(parsed) (and items match the
expected shape), otherwise ignore or clear the stored value to avoid breaking
apiKeys consumers.
♻️ Duplicate comments (2)
app/hooks/useConfigs.ts (2)

106-176: ⚠️ Potential issue | 🟠 Major

Cache and in-flight fetch state are still not scoped by request shape.

configState.inMemoryCache, configState.pendingFetch, and configState.pendingLoadMore are reused globally without {apiKey, pageSize} identity, so a key/page-size switch can reuse stale data or pending work from a different request context.

Suggested direction
+const requestScope = `${apiKey}|${pageSize ?? "all"}`;

- if (configState.inMemoryCache) { ...reuse... }
+ if (configState.inMemoryCache?.scope === requestScope) { ...reuse... }

- configState.pendingFetch = (async () => { ... })()
+ configState.pendingFetchByScope[requestScope] = (async () => { ... })()

- saveCache(newCache);
+ saveCache({ ...newCache, scope: requestScope });

Also apply the same scope check to localStorage cache restore and pendingLoadMore.

Also applies to: 180-226, 409-433

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/hooks/useConfigs.ts` around lines 106 - 176, The cache/pending state
(configState.inMemoryCache, configState.pendingFetch,
configState.pendingLoadMore) is global and not keyed by request identity,
causing cross-request reuse; update the code to namespace these entries by a
request key derived from the request shape (e.g.,
`${apiKey}|pageSize=${pageSize}`) and use that key whenever reading/writing the
in-memory cache, restoring localStorage (loadCache), scheduling background
validation (scheduleBackgroundValidation), and checking/setting
pendingFetch/pendingLoadMore; ensure the same keying is applied where
localStorage is restored and where pendingLoadMore is used so loadMore
operations and cached results are only reused for the matching {apiKey,pageSize}
request context.

304-382: ⚠️ Potential issue | 🟠 Major

Add missing auth dependencies to memoized callbacks.

loadSingleVersion uses apiKey (Line 315) but excludes it from deps, and loadMoreConfigs reads auth-derived key (Line 391) without auth deps. These can hold stale credentials after hydration/key switches.

Suggested fix
-  const loadSingleVersion = useCallback(
+  const loadSingleVersion = useCallback(
     async (config_id: string, version: number): Promise<SavedConfig | null> => {
       ...
     },
-    [configs],
+    [configs, apiKey],
   );

   const loadMoreConfigs = useCallback(async () => {
     if (!configState.allConfigMeta || configState.allConfigMeta.length === 0)
       return;
-    const apiKey = activeKey?.key;
     if (!apiKey) return;
     ...
-  }, [configs, pageSize]);
+  }, [configs, pageSize, apiKey]);

Also applies to: 388-450

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/hooks/useConfigs.ts` around lines 304 - 382, The memoized callback
loadSingleVersion currently closes over apiKey (and other auth-derived values)
but doesn't list them in its dependency array, which can lead to stale
credentials after hydration or key changes; update the useCallback dependency
array for loadSingleVersion to include apiKey and any other auth-derived values
it reads, and likewise update loadMoreConfigs and any other memoized functions
that read the auth-derived key to include that key (e.g., apiKey or auth.token)
and related auth/context variables in their deps so the callbacks are recreated
when credentials change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@app/lib/context/AuthContext.tsx`:
- Around line 32-36: The code reads localStorage.getItem(STORAGE_KEY) and
JSON.parse(...) then unconditionally calls setApiKeys, but JSON.parse may return
non-array values; update the try block in AuthContext (where STORAGE_KEY and
setApiKeys are used) to parse the stored value, verify it's an array (and
optionally validate expected item shape), and only call setApiKeys(parsed) when
Array.isArray(parsed) (and items match the expected shape), otherwise ignore or
clear the stored value to avoid breaking apiKeys consumers.

---

Duplicate comments:
In `@app/hooks/useConfigs.ts`:
- Around line 106-176: The cache/pending state (configState.inMemoryCache,
configState.pendingFetch, configState.pendingLoadMore) is global and not keyed
by request identity, causing cross-request reuse; update the code to namespace
these entries by a request key derived from the request shape (e.g.,
`${apiKey}|pageSize=${pageSize}`) and use that key whenever reading/writing the
in-memory cache, restoring localStorage (loadCache), scheduling background
validation (scheduleBackgroundValidation), and checking/setting
pendingFetch/pendingLoadMore; ensure the same keying is applied where
localStorage is restored and where pendingLoadMore is used so loadMore
operations and cached results are only reused for the matching {apiKey,pageSize}
request context.
- Around line 304-382: The memoized callback loadSingleVersion currently closes
over apiKey (and other auth-derived values) but doesn't list them in its
dependency array, which can lead to stale credentials after hydration or key
changes; update the useCallback dependency array for loadSingleVersion to
include apiKey and any other auth-derived values it reads, and likewise update
loadMoreConfigs and any other memoized functions that read the auth-derived key
to include that key (e.g., apiKey or auth.token) and related auth/context
variables in their deps so the callbacks are recreated when credentials change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32af3b65-7588-42a9-9a02-008e6899ff77

📥 Commits

Reviewing files that changed from the base of the PR and between 8a63136 and 1d1c1d3.

📒 Files selected for processing (2)
  • app/hooks/useConfigs.ts
  • app/lib/context/AuthContext.tsx

@Ayush8923 Ayush8923 merged commit 8076d1f into main Mar 26, 2026
2 checks passed
@Ayush8923 Ayush8923 deleted the feat/refactor-routes-phase-2 branch March 26, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants