Skip to content

Commit 4679acc

Browse files
committed
centralize auth loading and optimize query caching
1 parent babd32c commit 4679acc

17 files changed

Lines changed: 71 additions & 65 deletions

File tree

apps/server/src/lib/auth.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ export const auth: Auth = betterAuth({
6565
await env.SESSION_KV.delete(key);
6666
},
6767
},
68+
session: {
69+
cookieCache: {
70+
enabled: env.ALCHEMY_STAGE !== "dev", // Disable in dev to avoid stale data
71+
maxAge: 5 * 60, // 5 minutes
72+
},
73+
},
6874
rateLimit: {
6975
storage: "secondary-storage",
7076
},

apps/web/src/components/navigation/user-menu.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useQuery } from "@tanstack/react-query";
21
import { useNavigate } from "@tanstack/react-router";
32
import {
43
BookText,
@@ -8,6 +7,7 @@ import {
87
SettingsIcon,
98
UserLock,
109
} from "lucide-react";
10+
import { useAuth } from "@/web/components/auth-provider";
1111
import { Button } from "@/web/components/ui/button";
1212
import {
1313
DropdownMenu,
@@ -17,10 +17,9 @@ import {
1717
DropdownMenuSeparator,
1818
DropdownMenuTrigger,
1919
} from "@/web/components/ui/dropdown-menu";
20+
import { useProfile } from "@/web/hooks/use-profile";
2021
import { authClient } from "@/web/lib/auth-client";
21-
import { useAuth } from "@/web/lib/auth-context";
2222
import { SITE_GITHUB } from "@/web/lib/constants";
23-
import { orpc } from "@/web/lib/orpc";
2423

2524
const getFirstName = (
2625
user: { name?: string | null; email?: string | null } | null | undefined,
@@ -48,13 +47,7 @@ export default function UserMenu() {
4847
});
4948
};
5049

51-
const user = useQuery({
52-
...orpc.profile.getProfile.queryOptions(),
53-
enabled: Boolean(auth.session?.user),
54-
refetchOnWindowFocus: true,
55-
refetchOnMount: true,
56-
staleTime: 0,
57-
});
50+
const user = useProfile();
5851

5952
const displayFirstName = user.data
6053
? (getFirstName(user.data) ?? "User")

apps/web/src/hooks/use-profile.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { useAuth } from "@/web/components/auth-provider";
3+
import { orpc } from "@/web/lib/orpc";
4+
5+
/**
6+
* Shared hook for accessing user profile data across the application.
7+
*
8+
* Caching Strategy:
9+
* - 5 minute staleTime (matches server cookie cache duration)
10+
* - No automatic refetching (refetchOnMount, refetchOnWindowFocus disabled)
11+
* - Manual invalidation on profile mutations only
12+
*
13+
* This prevents duplicate network calls and reduces server load while
14+
* keeping data fresh when it actually changes.
15+
*/
16+
export function useProfile() {
17+
const auth = useAuth();
18+
const isAuthenticated = Boolean(auth.session?.user);
19+
20+
return useQuery(
21+
orpc.profile.getProfile.queryOptions({
22+
enabled: isAuthenticated,
23+
staleTime: 5 * 60 * 1000, // 5 minutes
24+
refetchOnMount: false,
25+
refetchOnWindowFocus: false,
26+
}),
27+
);
28+
}

apps/web/src/hooks/use-user-settings.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
22
import { useCallback, useEffect } from "react";
33
import type { RouterInputs, RouterOutputs } from "@/server/lib/router";
4-
import { useAuth } from "@/web/lib/auth-context";
4+
import { useAuth } from "@/web/components/auth-provider";
55
import { orpc } from "@/web/lib/orpc";
66
import {
77
getStoredChatWidth,
@@ -23,8 +23,14 @@ const DEFAULT_USER_SETTINGS: UserSettings = {
2323

2424
/**
2525
* Shared hook for accessing user settings across the application.
26-
* This hook consolidates all user settings queries into a single query,
27-
* preventing excessive refetches and potential infinite loops.
26+
*
27+
* Caching Strategy:
28+
* - 5 minute staleTime (matches server cookie cache duration)
29+
* - No automatic refetching (refetchOnMount disabled)
30+
* - Optimistic updates on mutations for instant UI feedback
31+
*
32+
* This consolidates all user settings queries into a single query,
33+
* preventing excessive refetches and reducing server load.
2834
*/
2935
export function useUserSettings() {
3036
const auth = useAuth();
@@ -33,8 +39,9 @@ export function useUserSettings() {
3339
const query = useQuery(
3440
orpc.settings.get.queryOptions({
3541
enabled: isAuthenticated,
36-
staleTime: 30_000,
42+
staleTime: 5 * 60 * 1000, // 5 minutes
3743
refetchOnMount: false,
44+
refetchOnWindowFocus: false,
3845
retry: false,
3946
placeholderData: () => {
4047
const storedChatWidth = getStoredChatWidth();

apps/web/src/lib/route-guards.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,38 @@
11
import { redirect } from "@tanstack/react-router";
2-
import type { AuthContextValue } from "@/web/lib/auth-context";
2+
import type { AuthContextValue } from "@/web/components/auth-provider";
33

44
interface RedirectIfAuthenticatedOptions {
55
auth: AuthContextValue;
66
to: string;
7-
replace?: boolean;
87
}
98

109
export function redirectIfAuthenticated({
1110
auth,
1211
to,
13-
replace,
1412
}: RedirectIfAuthenticatedOptions) {
15-
if (auth.isPending) {
16-
return;
17-
}
18-
1913
if (auth.isAuthenticated) {
2014
throw redirect({
2115
to,
22-
replace: replace ?? true,
16+
replace: true,
2317
});
2418
}
2519
}
2620

2721
interface RequireAuthenticatedOptions {
2822
auth: AuthContextValue;
2923
location: { href?: string | null; pathname?: string | null };
30-
signInPath?: string;
31-
redirectOverride?: string;
32-
replace?: boolean;
3324
}
3425

3526
export function requireAuthenticated({
3627
auth,
3728
location,
38-
signInPath,
39-
redirectOverride,
40-
replace,
4129
}: RequireAuthenticatedOptions) {
42-
if (auth.isPending) {
43-
return;
44-
}
45-
4630
if (!auth.isAuthenticated) {
47-
const redirectTarget =
48-
redirectOverride ?? location.href ?? location.pathname ?? "/";
31+
const redirectTarget = location.href ?? location.pathname ?? "/";
4932

5033
throw redirect({
51-
to: signInPath ?? "/auth/sign-in",
52-
replace: replace ?? true,
34+
to: "/auth/sign-in",
35+
replace: true,
5336
search: {
5437
redirect: redirectTarget,
5538
},

apps/web/src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { QueryClientProvider } from "@tanstack/react-query";
22
import { createRouter, RouterProvider } from "@tanstack/react-router";
33
import { useMemo } from "react";
44
import ReactDOM from "react-dom/client";
5-
import { AuthProvider, useAuth } from "@/web/lib/auth-context";
5+
import { AuthProvider, useAuth } from "@/web/components/auth-provider";
66
import { orpc, queryClient } from "./lib/orpc";
77
import { routeTree } from "./routeTree.gen";
88

apps/web/src/routes/__root.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
useRouterState,
77
} from "@tanstack/react-router";
88
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
9+
import type { AuthContextValue } from "@/web/components/auth-provider";
910
import { BackgroundLayout } from "@/web/components/background";
1011
import { ErrorBoundary } from "@/web/components/error-boundary";
1112
import Header from "@/web/components/navigation/header";
1213
import { NotFound } from "@/web/components/not-found";
1314
import { ThemeProvider } from "@/web/components/theme-provider";
1415
import { Toaster } from "@/web/components/ui/sonner";
15-
import type { AuthContextValue } from "@/web/lib/auth-context";
1616
import type { orpc } from "@/web/lib/orpc";
1717
import "@/web/index.css";
1818
import { useEffect } from "react";
@@ -30,7 +30,7 @@ export const Route = createRootRouteWithContext<RouterAppContext>()({
3030
});
3131

3232
function RootComponent() {
33-
// force scroll to top on route change
33+
// force scroll to top on route change -- router default behavior doesn't seem to be working?
3434
const router = useRouterState();
3535

3636
useEffect(() => {

apps/web/src/routes/auth/-components/sign-in-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useNavigate } from "@tanstack/react-router";
33
import { ExternalLink } from "lucide-react";
44
import { useEffect, useState } from "react";
55
import { toast } from "sonner";
6+
import { useAuth } from "@/web/components/auth-provider";
67
import { Button } from "@/web/components/ui/button";
78
import { Input } from "@/web/components/ui/input";
89
import {
@@ -12,7 +13,6 @@ import {
1213
} from "@/web/components/ui/input-otp";
1314
import { useAppForm } from "@/web/components/ui/tanstack-form";
1415
import { authClient } from "@/web/lib/auth-client";
15-
import { useAuth } from "@/web/lib/auth-context";
1616
import { SIGN_IN_FORM, SOCIAL_PROVIDERS } from "@/web/lib/constants";
1717
import { signInEmailSchema, signInOtpSchema } from "@/web/lib/validators";
1818
import { GitHubIcon, GoogleIcon } from "./social-sign-in-icons";

apps/web/src/routes/chat/-components/chat-settings-dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function ChatSettingsDialog({
5959
// Models query
6060
const modelsQuery = useQuery(
6161
orpc.models.list.queryOptions({
62-
staleTime: 60_000,
62+
staleTime: 5 * 60 * 1000, // 5 minutes
6363
}),
6464
);
6565

0 commit comments

Comments
 (0)