ai-wpa/todo/PERFORMANCE_OPTIMIZATION.md

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