perf: add auth middleware to speed up navigation changes#298
perf: add auth middleware to speed up navigation changes#298
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements middleware-based authentication to improve page navigation performance by eliminating redundant /user/me API calls. The middleware validates sessions once per navigation and caches user data in headers, while converted pages use server-side data fetching with React Query hydration.
Changes:
- Added authentication middleware with 1-hour session caching to reduce redundant API calls
- Created server-side helper to extract user data from request headers
- Converted multiple pages to server components with proper data prefetching patterns
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend-v2/src/middleware.ts | Implements middleware with session caching and auth enforcement |
| frontend-v2/src/lib/server-user.ts | Provides helper to extract user from middleware-injected headers |
| frontend-v2/src/app/users/user-form.tsx | Fixes controlled/uncontrolled Select component state |
| frontend-v2/src/app/users/page.tsx | Restructures hydration boundary and parallelizes prefetch queries |
| frontend-v2/src/app/users/new/page.tsx | Moves hydration boundary inside ContentWrapper |
| frontend-v2/src/app/users/[id]/page.tsx | Removes redundant chapter list prefetch and restructures hydration |
| frontend-v2/src/app/interest/generator/page.tsx | Removes unnecessary prefetching infrastructure |
| frontend-v2/src/app/event/page.tsx | Removes Suspense wrapper |
| frontend-v2/src/app/event/[id]/page.tsx | Removes prefetching infrastructure |
| frontend-v2/src/app/coaching/[id]/page.tsx | Removes prefetching infrastructure |
| frontend-v2/src/app/authed-page-layout.tsx | Replaces fetchSession with getServerUser helper |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
frontend-v2/src/middleware.ts:1
- This simple hash function is vulnerable to collisions and could allow different cookie strings to map to the same cache entry, potentially exposing one user's session to another. Use a cryptographic hash function like SHA-256 or a secure library to hash the cookie string.
import { NextResponse } from 'next/server'
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Get cookies from request | ||
| const cookieHeader = request.headers.get('cookie') || '' | ||
|
|
||
| // Fetch session (cached) | ||
| const session = await getCachedSession(cookieHeader) | ||
|
|
||
| // Redirect to login if no user |
There was a problem hiding this comment.
these 3 comments seem p obvious
There was a problem hiding this comment.
would be great if you can extract an interface for a generic cache and put it in another file, for separation of concerns.
| * Only works in server components. | ||
| */ | ||
| export async function getServerUser(): Promise<User> { | ||
| const headersList = await headers() |
There was a problem hiding this comment.
this can be inlined if only used once and the variable's name doesn't provide context
|
|
||
| if (!userHeader) { | ||
| throw new Error( | ||
| 'User not found in headers. This should not happen if middleware is working correctly.', |
There was a problem hiding this comment.
remove " This should not happen if middleware is working correctly." - this would be easy to figure out by looking through references of SERVER_USER_HEADER and it already says on line 7 that it's set by middleware
| }), | ||
| queryClient.prefetchQuery({ | ||
| queryKey: [API_PATH.CHAPTER_LIST], | ||
| queryFn: apiClient.getChapterList, |
There was a problem hiding this comment.
did you mean to not put this one back? chapter list is used to populate the chapter dropdown for a user
| <HydrationBoundary state={dehydrate(queryClient)}> | ||
| <Navbar /> | ||
| <ContentWrapper size="sm" className="gap-8"> | ||
| <Navbar /> |
There was a problem hiding this comment.
btw i was thinking of prefetching the chapter list for SSR of the navbar's chapter switcher's chapter list, in which case the Navbar would need to be in HydrationBoundary. but maybe that too should not use tanstack query but get headers set by the middleware? someday
Performance: Fix slow navigation by implementing middleware-based auth
Problem
Page navigation was taking 500ms-2s because we were hitting the
/user/meAPI endpoint on every single route change. Every page was callingfetchSession()independently, which meant auth overhead on every click.Solution
Implemented proper Next.js App Router authentication pattern using middleware with session caching:
New middleware layer (
middleware.ts):New server helper (
server-user.ts):fetchSession()calls across the appConverted pages to server components:
HydrationBoundaryImpact
/user/mecalls)Other fixes