Skip to content

Comments

perf: remove global fetchUser from root beforeLoad to improve TTFB#1939

Merged
yujonglee merged 2 commits intomainfrom
devin/1764215899-remove-root-fetchuser
Nov 27, 2025
Merged

perf: remove global fetchUser from root beforeLoad to improve TTFB#1939
yujonglee merged 2 commits intomainfrom
devin/1764215899-remove-root-fetchuser

Conversation

@yujonglee
Copy link
Contributor

@yujonglee yujonglee commented Nov 27, 2025

Summary

Removes the global fetchUser() Supabase call from the root route's beforeLoad, which was blocking every page request (including static marketing pages like /, /pricing, /blog, /docs). Auth is now only fetched for /app/* routes that actually need it.

Before: Every page request → Netlify serverless function → Supabase auth call → HTML response
After: Marketing pages → Direct HTML response (no auth call); /app/* routes → Auth call only when needed

Updates since last revision

  • Fixed TypeScript error in file-transcription.tsx - this page was using Route.useRouteContext() to get user, which broke after removing user from root context. Now uses client-side auth via getSupabaseBrowserClient() instead, which preserves the "sign in to see transcript" gating behavior without blocking SSR.

Review & Testing Checklist for Human

  • Verify /app/account page works - Navigate to /app/account while logged in and confirm user email displays correctly. This is the highest risk area since it depends on TanStack Router context inheritance from the parent route.
  • Verify /app/* auth redirect works - Try accessing /app/account while logged out and confirm it redirects to /
  • Verify /file-transcription page works - Upload a file, verify the "Sign in" button appears when logged out, and transcript displays when logged in. Note: there may be a brief flash before auth state loads since it's now client-side.
  • Verify marketing pages load - Confirm /, /pricing, /blog, /docs all load correctly without errors
  • Measure TTFB improvement - Compare Time To First Byte on production before/after for a marketing page like /pricing

Test Plan

  1. Deploy to preview environment
  2. Open DevTools Network tab
  3. Load /pricing and note TTFB (should be significantly faster, no Supabase call in waterfall)
  4. Log in via /auth, then navigate to /app/account - verify user info displays
  5. Log out, try to access /app/account directly - verify redirect to /
  6. Visit /file-transcription, upload a file - verify auth gating works (sign in prompt when logged out, transcript when logged in)

Notes

  • Prerendering remains disabled per user request (one fix at a time)
  • The Header component doesn't actually use context.user - it only uses platform detection, so no changes needed there
  • The /file-transcription page now loads auth client-side, so there may be a brief moment where user state is unknown on initial render

Requested by: yujonglee (@yujonglee)
Devin session: https://app.devin.ai/sessions/9b81f9c911944213991f6b368b4b45af

- Remove fetchUser call from __root.tsx beforeLoad that was blocking every page request
- Move auth check to /app route which is the only route tree that needs it
- Marketing pages (/, /pricing, /blog, /docs) no longer wait for Supabase auth call
- This should significantly improve TTFB for all marketing pages on Netlify

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 27, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 3cf1121
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6927cf672fea64000808f2aa
😎 Deploy Preview https://deploy-preview-1939--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 27, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 3cf1121
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6927cf67f64dec00086a4ccb
😎 Deploy Preview https://deploy-preview-1939--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

📝 Walkthrough

Walkthrough

Moved server-side user fetching: removed root-level beforeLoad and fetchUser import; app-level route now calls fetchUser in its beforeLoad and app index adds a loader passing context.user; a file-transcription component now fetches user on the client via Supabase in an effect; one parent view route loader was removed.

Changes

Cohort / File(s) Summary
Root route cleanup
apps/web/src/routes/__root.tsx
Removed beforeLoad that fetched the user and removed the fetchUser import.
App route authentication
apps/web/src/routes/_view/app/route.tsx
Replaced context-based check with fetchUser() in beforeLoad; redirects to / if no user and returns { user } as route data.
App index loader
apps/web/src/routes/_view/app/index.tsx
Added loader: async ({ context }) => ({ user: context.user }) to expose context.user as route data.
Parent view route
apps/web/src/routes/_view/route.tsx
Removed loader property that previously mapped context.user into route data.
Client-side user load
apps/web/src/routes/_view/file-transcription.tsx
Replaced Route.useRouteContext() usage with local user state and useEffect that calls getSupabaseBrowserClient() to load the user; added mounted-flag to guard state updates.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Browser
    participant Router as Router
    participant AppRoute as _view/app Route
    participant Auth as fetchUser()
    participant AppIndex as _view/app index loader
    participant FileComp as file-transcription component
    rect `#E6F4EA`
    Client->>Router: Navigate to /_view/app
    Router->>AppRoute: Run beforeLoad
    AppRoute->>Auth: call fetchUser()
    Auth-->>AppRoute: returns user or null
    alt user exists
        AppRoute-->>Router: return { user }
        Router->>AppIndex: run loader (context.user)
        AppIndex-->>Router: return { user }
        Router-->>Client: render with server-provided user
    else no user
        AppRoute->>Router: throw redirect to "/"
        Router-->>Client: navigate to /
    end
    end
    note over FileComp: client-side component loads user on mount
    Client->>FileComp: mount
    FileComp->>Auth: getSupabaseBrowserClient() -> request current user
    Auth-->>FileComp: returns user or null
    FileComp-->>Client: set local user state (guarded by mounted flag)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Check fetchUser import and usage in apps/web/src/routes/_view/app/route.tsx.
  • Verify loader in apps/web/src/routes/_view/app/index.tsx receives context.user as expected.
  • Confirm removal of root beforeLoad and _view/route loader doesn't break middleware or downstream expectations.
  • Review client-side effect in file-transcription.tsx for proper cleanup and SSR boundaries.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: removing the global fetchUser call from the root beforeLoad hook to improve Time To First Byte (TTFB) performance.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the performance optimization, the specific changes made, testing requirements, and potential issues discovered and addressed.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1764215899-remove-root-fetchuser

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

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
apps/web/src/routes/_view/app/index.tsx (1)

4-10: Loader correctly projects user; consider removing now-redundant auth check

Given that the parent /_view/app route’s beforeLoad now fetches the user and returns { user } into the router context (per TanStack Router’s “modifying router context” semantics), context.user should already be non-null whenever this index route runs. In that case:

  • loader: async ({ context }) => ({ user: context.user }) is a cheap, correct way to expose the user as loader data.
  • The local beforeLoad guard that redirects to /app/account on missing context.user likely never triggers anymore, since an unauthenticated request should already have been redirected by the parent route’s beforeLoad.

If that matches the intended flow, you can optionally drop this child beforeLoad entirely (and rely solely on the parent) or align its redirect target with the parent for consistency.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0be4ff and 4ada8e6.

📒 Files selected for processing (4)
  • apps/web/src/routes/__root.tsx (0 hunks)
  • apps/web/src/routes/_view/app/index.tsx (1 hunks)
  • apps/web/src/routes/_view/app/route.tsx (1 hunks)
  • apps/web/src/routes/_view/route.tsx (0 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/__root.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/app/index.tsx
  • apps/web/src/routes/_view/app/route.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/app/route.tsx (3)
apps/web/src/routes/_view/app/index.tsx (1)
  • Route (3-10)
apps/web/src/routes/_view/app/account.tsx (1)
  • Route (11-14)
apps/web/src/functions/auth.ts (1)
  • fetchUser (56-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (1)
apps/web/src/routes/_view/app/route.tsx (1)

3-11: I'll help you verify the two key points raised in the review comment. Let me start by checking the usage of fetchUser and understanding the broader context.
<function_calls>

#!/bin/bash

Check all usages of fetchUser in the web app

rg "fetchUser" apps/web/src -n -A 2 -B 2


</function_calls>

#!/bin/bash

Locate root route file to check for global auth fetching

find apps/web/src -name ".tsx" -o -name ".ts" | grep -E "(root|root|layout)" | head -20


</function_calls>
<function_calls>

#!/bin/bash

Check the root route structure

ls -la apps/web/src/routes/


</function_calls>

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@argos-ci
Copy link

argos-ci bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 3 changed Nov 27, 2025, 4:13 AM

Copy link
Contributor

@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: 0

🧹 Nitpick comments (2)
apps/web/src/routes/_view/file-transcription.tsx (2)

23-45: Consider using useQuery for better state management.

Per coding guidelines, "Use useQuery/useMutation (from tanstack-query) instead for 99% of cases." This would provide:

  • Built-in loading state (preventing the sign-in button from flickering while auth loads)
  • Better error handling (currently all errors are caught silently, which could hide authentication or network issues)
  • Caching and automatic refetching capabilities

Apply this refactor to use useQuery:

+import { useQuery } from "@tanstack/react-query";
+
 function Component() {
-  const [user, setUser] = useState<{ email?: string } | null>(null);
-
-  useEffect(() => {
-    let isMounted = true;
-    async function loadUser() {
-      try {
-        const supabase = getSupabaseBrowserClient();
-        const { data } = await supabase.auth.getUser();
-        if (!isMounted) return;
-        if (data.user?.email) {
-          setUser({ email: data.user.email });
-        } else {
-          setUser(null);
-        }
-      } catch {
-        if (isMounted) setUser(null);
-      }
-    }
-    loadUser();
-    return () => {
-      isMounted = false;
-    };
-  }, []);
+  const { data: user } = useQuery({
+    queryKey: ['user'],
+    queryFn: async () => {
+      const supabase = getSupabaseBrowserClient();
+      const { data, error } = await supabase.auth.getUser();
+      if (error) throw error;
+      return data.user?.email ? { email: data.user.email } : null;
+    },
+    retry: false,
+  });

As per coding guidelines.


155-162: User-gated transcript display is correctly implemented.

The conditional rendering based on user authentication state works correctly for the current sample transcript implementation.

Note for future Deepgram integration: When implementing the actual transcription service (line 59), ensure the transcription API endpoint enforces authentication server-side. The current client-side gating (user ? transcript : null) is appropriate for display logic but should not be the only security layer for sensitive transcription data.

Also applies to: 174-176, 180-186

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ada8e6 and 3cf1121.

📒 Files selected for processing (1)
  • apps/web/src/routes/_view/file-transcription.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/file-transcription.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/file-transcription.tsx (1)
apps/web/src/functions/supabase.ts (1)
  • getSupabaseBrowserClient (7-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (1)
apps/web/src/routes/_view/file-transcription.tsx (1)

2-2: LGTM!

The import additions are necessary for the new client-side user fetching logic and are correctly placed.

Also applies to: 13-13

@yujonglee yujonglee merged commit eafeb1d into main Nov 27, 2025
13 of 14 checks passed
@yujonglee yujonglee deleted the devin/1764215899-remove-root-fetchuser branch November 27, 2025 05:36
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