8.1 KiB
Performance Optimization Checklist
Current Status: Largest Contentful Paint (LCP) = 2.6s
Target: LCP < 1.2s (excellent), < 2.5s (good)
Priority: 🔴 Critical - Blocking user experience
Instructions: Each task should be completed individually. AI should ask for user validation before marking as complete and moving to next task.
🚨 Critical Issues (Must Fix)
Task 1: Remove Blocking Startup Check from Layout
Impact: -1.5s LCP improvement
Status: ❌ Pending
File: app/layout.tsx
Lines: 37-40
Action: Remove the following blocking code from root layout:
// REMOVE THIS:
if (typeof window === 'undefined') {
const { runStartupChecks } = await import('@/lib/startup')
await runStartupChecks()
}
Validation: Page should load immediately without waiting for database connections
- TASK 1 COMPLETED ✅
Task 2: Create Background Startup Instrumentation
Impact: Maintain startup checks without blocking
Status: ❌ Pending
File: Create app/instrumentation.ts
Action: Create new file with background startup checks:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
import('./lib/startup').then(({ runStartupChecks }) => {
runStartupChecks().catch(console.error)
})
}
}
Validation: Startup checks run in background, visible in terminal logs
- TASK 2 COMPLETED ✅
Task 3: Create Redis User Caching Utility
Impact: -0.45s improvement
Status: ❌ Pending
File: Create lib/auth-cache.ts
Action: Create Redis caching utility:
import { connectRedis } from './redis'
const USER_CACHE_PREFIX = 'user:'
const CACHE_TTL = 300 // 5 minutes
export async function getCachedUser(userId: string) {
try {
const redis = await connectRedis()
const cached = await redis.get(`${USER_CACHE_PREFIX}${userId}`)
return cached ? JSON.parse(cached) : null
} catch (error) {
console.error('Redis cache get error:', error)
return null // Fallback to DB if Redis fails
}
}
export async function setCachedUser(userId: string, userData: any) {
try {
const redis = await connectRedis()
await redis.setex(`${USER_CACHE_PREFIX}${userId}`, CACHE_TTL, JSON.stringify(userData))
} catch (error) {
console.error('Redis cache set error:', error)
// Don't throw - caching is optional
}
}
export async function clearCachedUser(userId: string) {
try {
const redis = await connectRedis()
await redis.del(`${USER_CACHE_PREFIX}${userId}`)
} catch (error) {
console.error('Redis cache clear error:', error)
}
}
Validation: File created with proper Redis integration
- TASK 3 COMPLETED ✅
Task 4: Update /me Endpoint to Use Redis Cache
Impact: Reduce MongoDB calls by 80%
Status: ❌ Pending
File: app/api/auth/me/route.ts
Action: Replace the current GET function with:
import { getCachedUser, setCachedUser } from '@/lib/auth-cache'
export const GET = withAuth(async (request: NextRequest & { user?: any }) => {
try {
// 1. Check Redis cache first
const cached = await getCachedUser(request.user.userId)
if (cached) {
return NextResponse.json({
success: true,
data: { user: cached },
})
}
// 2. Only hit MongoDB if not cached
await connectDB()
const user = await User.findById(request.user.userId).select('-password -refreshToken').lean() // Better performance
if (!user) {
return NextResponse.json(
{ success: false, error: { message: 'User not found', code: 'USER_NOT_FOUND' } },
{ status: 404 }
)
}
// 3. Cache for next time
await setCachedUser(request.user.userId, user)
return NextResponse.json({
success: true,
data: { user },
})
} catch (error) {
console.error('Get user info error:', error)
return NextResponse.json(
{ success: false, error: { message: 'Internal server error', code: 'INTERNAL_ERROR' } },
{ status: 500 }
)
}
})
Validation: /me endpoint returns cached data on subsequent calls
- TASK 4 COMPLETED ✅
Task 5: Add Cache Invalidation to User Updates
Impact: Ensure cache consistency
Status: ❌ Pending
File: app/api/auth/refresh/route.ts
Action: Add cache clearing after user update:
// Add import at top:
import { setCachedUser } from '@/lib/auth-cache'
// After user.save(), add:
await setCachedUser(user._id.toString(), user.toJSON())
Validation: User cache updates when refresh token is used
- TASK 5 COMPLETED ✅
Task 6: Optimize Auth Context with localStorage
Impact: -0.25s improvement
Status: ❌ Pending
File: contexts/AuthContext.tsx
Lines: 157-180
Action: Replace the restoreSession function:
const restoreSession = async () => {
try {
setLoading(true)
// 1. Load from localStorage immediately (sync)
const cachedUser = localStorage.getItem('nextjs_user')
if (cachedUser) {
const userData = JSON.parse(cachedUser)
setUser(userData)
setHasCheckedAuth(true)
setLoading(false)
// 2. Validate in background (async)
apiCall('/me')
.then((data) => {
setUser(data.data.user)
localStorage.setItem('nextjs_user', JSON.stringify(data.data.user))
})
.catch(() => {
// Try refresh token if /me fails
apiCall('/refresh', { method: 'POST' })
.then((refreshData) => {
setUser(refreshData.data.user)
localStorage.setItem('nextjs_user', JSON.stringify(refreshData.data.user))
})
.catch(() => {
// Both failed, clear cache and user
localStorage.removeItem('nextjs_user')
setUser(null)
})
})
return
}
// 3. Only do full auth check if no cached user
const data = await apiCall('/me')
setUser(data.data.user)
localStorage.setItem('nextjs_user', JSON.stringify(data.data.user))
setHasCheckedAuth(true)
} catch (err) {
// If /me fails, try refresh token
try {
const refreshData = await apiCall('/refresh', { method: 'POST' })
setUser(refreshData.data.user)
localStorage.setItem('nextjs_user', JSON.stringify(refreshData.data.user))
setHasCheckedAuth(true)
} catch (refreshErr) {
// Both failed, user is not authenticated
setUser(null)
setHasCheckedAuth(true)
}
} finally {
setLoading(false)
}
}
Validation: Page loads show cached user immediately, then validates in background
- TASK 6 COMPLETED ✅
Task 7: Clear localStorage on Logout
Impact: Proper cache cleanup
Status: ❌ Pending
File: contexts/AuthContext.tsx
Function: logout
Action: Add localStorage clearing to logout function:
// In the logout function, add this line:
localStorage.removeItem('nextjs_user')
Validation: User data cleared from localStorage after logout
- TASK 7 COMPLETED ✅
Task 8: Add Database Query Optimization
Impact: -0.1s improvement
Status: ❌ Pending
File: app/api/auth/login/route.ts
Lines: 23
Action: Optimize user lookup query:
// Replace:
const user = await User.findOne({ email: validatedData.email.toLowerCase() })
// With:
const user = await User.findOne({ email: validatedData.email.toLowerCase() })
.lean()
.select('+password')
Validation: Login queries use optimized database access
- TASK 8 COMPLETED ✅
📊 Progress Tracking
Completed: 0/8 tasks
Expected LCP Improvement: 0s of 2.2s total
Current Status: Ready to start Task 1
🎯 Success Criteria
After completing all tasks:
- LCP: Should improve from 2.6s to ~0.4s
- Redis Cache Hit Rate: Should be >80% for /me endpoint
- Page Load: Should render immediately without database waits
- Auth Restoration: Should show cached user instantly
Next Action: Start with Task 1 - Remove Blocking Startup Check