Claude-skill-registry clerk-performance-tuning

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/clerk-performance-tuning" ~/.claude/skills/majiayu000-claude-skill-registry-clerk-performance-tuning && rm -rf "$T"
manifest: skills/data/clerk-performance-tuning/SKILL.md
source content

Clerk Performance Tuning

Overview

Optimize Clerk authentication for best performance and user experience.

Prerequisites

  • Clerk integration working
  • Performance monitoring in place
  • Understanding of application architecture

Instructions

Step 1: Optimize Middleware

// middleware.ts - Optimized configuration
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

// Pre-compile route matchers (done once at startup)
const isPublicRoute = createRouteMatcher([
  '/',
  '/sign-in(.*)',
  '/sign-up(.*)',
  '/api/public(.*)',
  '/api/webhooks(.*)'
])

// Exclude static files from middleware processing
export const config = {
  matcher: [
    // Skip all static files and images
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)'
  ]
}

export default clerkMiddleware(async (auth, request) => {
  // Quick return for public routes
  if (isPublicRoute(request)) {
    return
  }

  // Protect other routes
  await auth.protect()
})

Step 2: Implement User Data Caching

// lib/cached-user.ts
import { unstable_cache } from 'next/cache'
import { clerkClient, currentUser } from '@clerk/nextjs/server'

// Cache user data with Next.js cache
export const getCachedUser = unstable_cache(
  async (userId: string) => {
    const client = await clerkClient()
    return client.users.getUser(userId)
  },
  ['user-data'],
  {
    revalidate: 60, // 1 minute cache
    tags: ['users']
  }
)

// In-memory cache for very frequent lookups
const userCache = new Map<string, { data: any; expiry: number }>()

export async function getUserFast(userId: string) {
  const cached = userCache.get(userId)
  const now = Date.now()

  if (cached && cached.expiry > now) {
    return cached.data
  }

  const user = await getCachedUser(userId)
  userCache.set(userId, {
    data: user,
    expiry: now + 30000 // 30 seconds
  })

  return user
}

// Invalidate cache on user update
export function invalidateUserCache(userId: string) {
  userCache.delete(userId)
}

Step 3: Optimize Token Handling

// lib/optimized-auth.ts
'use client'
import { useAuth } from '@clerk/nextjs'
import { useRef } from 'react'

// Cache tokens to avoid repeated async calls
export function useOptimizedAuth() {
  const { getToken, userId, isLoaded } = useAuth()
  const tokenCache = useRef<{
    token: string | null
    expiry: number
  } | null>(null)

  const getCachedToken = async () => {
    const now = Date.now()

    // Return cached token if still valid (with 5 min buffer)
    if (tokenCache.current &&
        tokenCache.current.token &&
        tokenCache.current.expiry > now + 300000) {
      return tokenCache.current.token
    }

    // Get fresh token
    const token = await getToken()

    if (token) {
      // Parse expiry from JWT
      const payload = JSON.parse(atob(token.split('.')[1]))
      tokenCache.current = {
        token,
        expiry: payload.exp * 1000
      }
    }

    return token
  }

  return { getCachedToken, userId, isLoaded }
}

// Optimized fetch with token
export function useAuthFetch() {
  const { getCachedToken } = useOptimizedAuth()

  return async (url: string, options: RequestInit = {}) => {
    const token = await getCachedToken()

    return fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    })
  }
}

Step 4: Lazy Load Auth Components

// components/lazy-auth.tsx
'use client'
import dynamic from 'next/dynamic'
import { Suspense } from 'react'

// Lazy load heavy auth components
const UserButton = dynamic(
  () => import('@clerk/nextjs').then(mod => mod.UserButton),
  {
    loading: () => <div className="w-8 h-8 bg-gray-200 rounded-full animate-pulse" />,
    ssr: false
  }
)

const SignInButton = dynamic(
  () => import('@clerk/nextjs').then(mod => mod.SignInButton),
  {
    loading: () => <button className="btn" disabled>Sign In</button>,
    ssr: false
  }
)

export function LazyUserButton() {
  return (
    <Suspense fallback={<div className="w-8 h-8 bg-gray-200 rounded-full" />}>
      <UserButton afterSignOutUrl="/" />
    </Suspense>
  )
}

Step 5: Optimize Server Components

// app/dashboard/page.tsx
import { auth } from '@clerk/nextjs/server'
import { Suspense } from 'react'

// Use streaming for auth-dependent content
export default async function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>

      {/* Stream user-specific content */}
      <Suspense fallback={<UserDataSkeleton />}>
        <UserData />
      </Suspense>

      {/* Non-auth content renders immediately */}
      <StaticContent />
    </div>
  )
}

async function UserData() {
  const { userId } = await auth()

  // Parallel data fetching
  const [user, stats, notifications] = await Promise.all([
    getUser(userId!),
    getUserStats(userId!),
    getNotifications(userId!)
  ])

  return (
    <div>
      <UserProfile user={user} />
      <UserStats stats={stats} />
      <Notifications items={notifications} />
    </div>
  )
}

Step 6: Edge Runtime Optimization

// app/api/fast-auth/route.ts
import { auth } from '@clerk/nextjs/server'

// Use Edge runtime for faster cold starts
export const runtime = 'edge'

export async function GET() {
  const { userId } = await auth()

  if (!userId) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  // Edge-compatible response
  return Response.json({ userId })
}

Performance Metrics

OperationTargetOptimization
Middleware check< 10msRoute matcher pre-compilation
Token validation< 50msJWT caching
User fetch< 100msMulti-level caching
Page load (auth)< 200msStreaming + lazy load

Monitoring

// lib/performance-monitor.ts
export function measureAuthPerformance<T>(
  name: string,
  operation: () => Promise<T>
): Promise<T> {
  const start = performance.now()

  return operation().finally(() => {
    const duration = performance.now() - start
    console.log(`[Clerk Perf] ${name}: ${duration.toFixed(2)}ms`)

    // Send to monitoring (DataDog, etc.)
    if (duration > 100) {
      console.warn(`[Clerk Perf] Slow operation: ${name}`)
    }
  })
}

// Usage
const user = await measureAuthPerformance('getUser', () =>
  clerkClient.users.getUser(userId)
)

Output

  • Optimized middleware configuration
  • Multi-level caching strategy
  • Token management optimization
  • Lazy loading for auth components

Error Handling

IssueCauseSolution
Slow page loadsBlocking auth callsUse Suspense boundaries
High latencyNo cachingImplement token/user cache
Bundle sizeAll components loadedLazy load auth components
Cold startsNode runtimeUse Edge runtime

Resources

Next Steps

Proceed to

clerk-cost-tuning
for cost optimization strategies.