clerk-nextjs-patterns

Par clerk · skills

Patterns avancés Next.js - middleware, Server Actions, mise en cache avec

npx skills add https://github.com/clerk/skills --skill clerk-nextjs-patterns

Motifs Next.js

Version : Vérifiez package.json pour la version du SDK — voir la skill clerk pour le tableau des versions. Les différences Core 2 sont notées en ligne avec des callouts > **Core 2 ONLY (skip if current SDK):**.

Pour la configuration de base, voir la skill clerk-setup.

Qu'avez-vous besoin ?

Tâche Référence
Auth serveur vs client (auth() vs hooks) references/server-vs-client.md
Configurer le middleware (public-first vs protected-first) references/middleware-strategies.md
Protéger les Server Actions references/server-actions.md
Auth des routes API (401 vs 403) references/api-routes.md
Cacher les données d'auth (caching user-scoped) references/caching-auth.md

Références

Référence Description
references/server-vs-client.md await auth() vs hooks
references/middleware-strategies.md Public-first vs protected-first, proxy.ts (Next.js <=15: middleware.ts)
references/server-actions.md Protéger les mutations
references/api-routes.md 401 vs 403
references/caching-auth.md Caching user-scoped

Modèle Mental

Server vs Client = APIs d'auth différentes :

  • Server : await auth() depuis @clerk/nextjs/server (async !)
  • Client : hook useAuth() depuis @clerk/nextjs (sync)

Ne les mélangez jamais. Les Server Components utilisent les imports serveur, les Client Components utilisent les hooks.

Propriétés clés depuis auth() :

  • isAuthenticated — booléen, remplace le pattern !!userId
  • sessionStatus — 'active' | 'pending', pour détecter les tâches de session incomplètes
  • userId, orgId, orgSlug, has(), protect() — inchangés

Core 2 ONLY (skip if current SDK): isAuthenticated et sessionStatus ne sont pas disponibles. Vérifiez !!userId à la place.

Motif Minimal

// Server Component
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { isAuthenticated, userId } = await auth()  // MUST await!
  if (!isAuthenticated) return <p>Not signed in</p>
  return <p>Hello {userId}</p>
}

Core 2 ONLY (skip if current SDK): isAuthenticated n'est pas disponible. Utilisez if (!userId) à la place.

Rendu Conditionnel avec <Show>

Pour le rendu conditionnel côté client basé sur l'état d'auth. <Show> couvre à la fois les vérifications d'authentification et d'autorisation (feature, plan, role, permission) en un seul composant.

Vérification d'authentification :

import { Show } from '@clerk/nextjs'

<Show when="signed-in" fallback={<p>Please sign in</p>}>
  <Dashboard />
</Show>

Vérifications d'autorisation (B2B) :

// Feature-based (preferred — features can move between plans without redeploy)
<Show when={{ feature: 'analytics' }} fallback={<UpgradePrompt />}>
  <AnalyticsDashboard />
</Show>

// Permission-based (preferred over role-based for granular access)
<Show when={{ permission: 'org:invoices:create' }}>
  <NewInvoiceButton />
</Show>

// Plan-based (tier-level gating)
<Show when={{ plan: 'pro' }}>
  <ProFeatures />
</Show>

// Role-based (use sparingly — prefer permission)
<Show when={{ role: 'org:admin' }}>
  <AdminPanel />
</Show>

Callback pour logique complexe :

<Show when={(has) => has({ role: 'org:admin' }) || has({ role: 'org:billing_manager' })}>
  <BillingActions />
</Show>

Core 2 ONLY (skip if current SDK): <Show> n'existe pas. Pour l'authentification, utilisez <SignedIn> et <SignedOut>. Pour l'autorisation (role / permission), utilisez <Protect> avec les mêmes noms de props (role, permission, condition). Les variantes feature- et plan-based nécessitent Core 3. Voir la skill clerk-custom-ui, core-3/show-component.md pour le tableau de migration complet.

Pièges Courants

Symptôme Cause Solution
undefined userId dans Server Component await manquant await auth() pas auth()
Auth ne fonctionne pas sur les routes API matcher manquant Ajoutez '/(api|trpc)(.*)' à proxy.ts (Next.js <=15: middleware.ts)
Cache retourne les données du mauvais utilisateur userId manquant dans la clé Incluez userId dans la clé unstable_cache
Les mutations contournent l'auth Server Action non protégée Vérifiez auth() au début de l'action
Code d'erreur HTTP incorrect Confusion 401/403 401 = non signé, 403 = pas de permission

Session Tokens & Custom JWTs

getToken() pour les APIs externes

Passez un JWT personnalisé aux services tiers (Hasura, Supabase, etc.) en utilisant les templates JWT définis dans le dashboard Clerk.

Côté serveur (Server Component ou Route Handler) :

import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { getToken } = await auth()
  const token = await getToken({ template: 'hasura' })
  if (!token) return <p>Not authenticated</p>

  const res = await fetch('https://api.example.com/graphql', {
    headers: { Authorization: `Bearer ${token}` },
  })
  const data = await res.json()
  return <pre>{JSON.stringify(data)}</pre>
}

Côté client (Client Component) :

'use client'
import { useAuth } from '@clerk/nextjs'

export function DataFetcher() {
  const { getToken } = useAuth()

  async function fetchData() {
    const token = await getToken({ template: 'supabase' })
    if (!token) return

    const res = await fetch('https://api.example.com/data', {
      headers: { Authorization: `Bearer ${token}` },
    })
    return res.json()
  }

  return <button onClick={fetchData}>Fetch</button>
}

getToken() retourne null quand l'utilisateur n'est pas authentifié — vérifiez toujours null avant utilisation.

useSession() pour les données de session

Accédez aux métadonnées de session dans les client components :

'use client'
import { useSession } from '@clerk/nextjs'

export function SessionInfo() {
  const { session } = useSession()
  if (!session) return null

  return (
    <p>
      Session {session.id} — last active: {session.lastActiveAt.toISOString()}
    </p>
  )
}

Vérification JWT manuelle (sans middleware Clerk)

Pour les serveurs API autonomes qui reçoivent des session tokens Clerk depuis l'en-tête Authorization ou le cookie __session (same-origin).

En utilisant verifyToken de @clerk/backend (recommandé) :

import { verifyToken } from '@clerk/backend'

const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return res.status(401).json({ error: 'No token' })

try {
  const claims = await verifyToken(token, {
    jwtKey: process.env.CLERK_JWT_KEY,
  })
  // claims.sub = userId
} catch {
  return res.status(401).json({ error: 'Invalid token' })
}

En utilisant jsonwebtoken (quand vous ne pouvez pas utiliser @clerk/backend) :

import jwt from 'jsonwebtoken'

const publicKey = process.env.CLERK_PEM_PUBLIC_KEY!.replace(/\\n/g, '\n')
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) return res.status(401).json({ error: 'No token' })

try {
  const claims = jwt.verify(token, publicKey, { algorithms: ['RS256'] }) as jwt.JwtPayload
  // Manually check exp and nbf (jsonwebtoken does this automatically, but verify azp if needed)
  // claims.sub = userId
} catch {
  return res.status(401).json({ error: 'Invalid or expired token' })
}

Sources de token :

  • Requêtes same-origin : cookie __session (Clerk le définit automatiquement)
  • Cross-origin / mobile / API-to-API : en-tête Authorization: Bearer <token>

CRITIQUE : Vérifiez toujours les claims exp et nbf. verifyToken depuis @clerk/backend gère cela automatiquement ; avec jsonwebtoken brut, définissez ignoreExpiration: false (défaut) et assurez-vous que clockTolerance est minimal.

Voir Aussi

  • clerk-setup - Installation initiale de Clerk
  • clerk-orgs - Motifs B2B (org active, gating role/permission)
  • clerk-billing - Droits plan et feature avec has()
  • clerk-webhooks - Synchroniser les événements utilisateur/org vers votre base de données
  • clerk-custom-ui - Thématisation et personnalisation pour les composants intégrés

Docs

Next.js SDK

Skills similaires