Alle Artikel
Clerk-Benutzerereignisse, die durch einen signierten Webhook-Tunnel zu einem Next.js-App-Router-Endpunkt und einer lokalen Datenbank gelangen.
ClerkNext.jswebhooksuser sync

Clerk-Webhooks mit Next.js auf localhost testen

Um Clerk-Webhooks auf localhost in Next.js zu testen, erstellen Sie eine App Router POST-Route, stellen Sie Port 3000 mit einem HTTPS-Tunnel bereit, fügen Sie die öffentliche URL als Clerk-Webhook-Endpunkt hinzu und überprüfen Sie jede Anfrage mit verifyWebhook() bevor Sie Benutzerdaten synchronisieren. Halten Sie die Route in der Clerk-Middleware öffentlich: Das Signaturgeheimnis authentifiziert die Maschine-zu-Maschine-Anfrage, nicht eine Browsersitzung.

Bei Clerk sind Webhooks das richtige Synchronisierungstool

Clerk bleibt die Identitätsquelle der Wahrheit. Ein Webhook ist nützlich, wenn Ihre Anwendung eine lokale Projektion für Verknüpfungen, Suche, Berichterstellung, Autorisierungsmetadaten oder Integrationen benötigt, die Clerk nicht bei Bedarf abfragen können. Typische Ereignisse sind: user.created, user.updated, Und user.deleted.

Ein Webhook ist asynchron. Der Benutzer kann die Anmeldung abschließen, bevor Ihre Datenbankprojektion existiert, Zustellungen können wiederholt werden und Aktualisierungen können zeitnah eintreffen. Machen Sie die Prognose nicht zu Ihrer einzigen Quelle für Identitätsprüfungen unmittelbar nach der Anmeldung. Entwerfen Sie Lesevorgänge so, dass eine kurze Verzögerung toleriert wird, oder erstellen Sie die Anwendungszeile explizit im Benutzerfluss und lassen Sie sie von Webhooks abgleichen.

Erstellen Sie einen öffentlichen Next.js App Router-Endpunkt

Hinzufügen app/api/webhooks/clerk/route.ts. Aktueller Clerk Synchronisierungsanleitung verwendet verifyWebhook aus @clerk/nextjs/webhooks. Der Helfer verarbeitet die Anfrage, überprüft die Standard-Webhooks-Signatur und gibt typisierte Ereignisdaten zurück.

// app/api/webhooks/clerk/route.ts
import { verifyWebhook } from '@clerk/nextjs/webhooks';
import { NextRequest } from 'next/server';

export const runtime = 'nodejs';

export async function POST(request: NextRequest) {
  try {
    const event = await verifyWebhook(request);

    await processOnce(event, async () => {
      switch (event.type) {
        case 'user.created':
        case 'user.updated':
          await upsertClerkUser(event.data);
          break;
        case 'user.deleted':
          if (event.data.id) await archiveClerkUser(event.data.id);
          break;
      }
    });

    return new Response('accepted', { status: 200 });
  } catch (error) {
    console.error('Clerk webhook rejected', safeError(error));
    return new Response('invalid webhook', { status: 400 });
  }
}

Standardmäßig liest der Helfer CLERK_WEBHOOK_SIGNING_SECRET. Clerk VerifyWebhook-Referenz erlaubt auch eine explizite signingSecret Option, aber die Umgebungskonfiguration vermeidet die Einbettung des Geheimnisses in die Quelle.

Schließen Sie die Webhook-Route vom Sitzungsschutz aus

Webhook-Anfragen übertragen nicht die Clerk-Sitzung Ihres Benutzers. Wenn Middleware aufruft auth.protect() Für jeden API-Pfad erhält Clerks Lieferung eine Umleitung, 401 oder 404, bevor die Signaturüberprüfung ausgeführt wird. Definieren Sie explizit geschützte Anwendungsrouten und verlassen Sie sie /api/webhooks/clerk öffentlich.

// middleware.ts for Next.js 15 and earlier
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher([
  '/dashboard(.*)',
  '/api/private(.*)',
]);

export default clerkMiddleware(async (auth, request) => {
  if (isProtectedRoute(request)) await auth.protect();
});

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico)).*)',
    '/(api|trpc)(.*)',
  ],
};

Clerk Webhook-Debugging-Anleitung ruft ausdrücklich den Ausschluss von Webhook-Routen auf. In neueren Next.js-Versionen kann die Konvention verwendet werden proxy.ts; Befolgen Sie die in Ihrem Projekt installierte Clerk-Versionsanleitung. Öffentlich bedeutet nicht vertrauenswürdig: verifyWebhook() ist vor jeder Aktion zwingend erforderlich.

Verbinden Sie Clerk mit localhost

  1. Laufen npm run dev und stellen Sie sicher, dass die Next.js-App Port 3000 überwacht.
  2. Laufen npx portpreview 3000.
  3. Erstellen Sie im Clerk-Dashboard einen Webhook-Endpunkt mit https://your-subdomain.portpreview.dev/api/webhooks/clerk.
  4. Wählen Sie nur die erforderlichen Benutzer-, Sitzungs-, Organisations- oder E-Mail-Ereignisse aus.
  5. Kopieren Sie das Signaturgeheimnis des Endpunkts nach CLERK_WEBHOOK_SIGNING_SECRET in Ihrer lokalen Umgebung und starten Sie Next.js neu.
  6. Öffnen Sie die Registerkarte „Testen“ des Endpunkts und wählen Sie „ user.createdund wählen Sie Beispiel senden.
  7. Bestätigen Sie, dass der Versuch „Erfolgreich“ lautet, Ihre lokale Route 200 zurückgegeben hat und die erwartete Datenbankzeile einmal geändert wurde.

Die Tunnel-URL muss für nachfolgende Beispiele aktiv bleiben. Wenn es sich ändert, aktualisieren Sie den Endpunkt. Verwenden Sie das Signaturgeheimnis eines Produktionsendpunkts nicht für lokale Tests wieder. Erstellen Sie umgebungsspezifische Endpunkte, damit Rotation und Prüfverlauf klar bleiben.

Modellieren Sie die Benutzersynchronisierung sorgfältig

Verwenden Sie die Clerk-Benutzer-ID als externen Schlüssel

Speichern user_... in einem Unikat clerk_user_id Spalte. Upsert auf diesem Schlüssel, also ein erneuter Versuch user.created konvergiert statt zu scheitern. Behalten Sie Ihren eigenen internen Primärschlüssel, wenn andere Tabellen bereits darauf verweisen.

Wählen Sie bewusst die primäre E-Mail-Adresse

Die Benutzerdaten des Clerks enthalten E-Mail-Adressdatensätze und eine primäre E-Mail-Adress-ID. Lösen Sie den Primärdatensatz anhand der ID auf, anstatt das erste Array-Element zu verwenden. E-Mail kann sich ändern; Verwenden Sie ihn nicht als unveränderlichen Fremdschlüssel.

Entscheiden Sie, was Löschen bedeutet

A user.deleted Das Ereignis enthält möglicherweise weniger Daten als das Ereignis „Erstellen“ oder „Aktualisieren“. Verwenden Sie die ID zum Anonymisieren, zum vorläufigen Löschen oder zum Starten eines Aufbewahrungsworkflows gemäß Ihrer Richtlinie. Blindes, kaskadierendes Löschen kann Abrechnungs- oder Prüfdatensätze zerstören, deren Aufbewahrung Sie aufgrund gesetzlicher Vorschriften aufbewahren müssen.

Spiegeln Sie nicht alles

Behalten Sie nur die Felder bei, die Ihre Anwendung benötigt. Jedes kopierte Profilfeld führt zu einer Datenschutz-, Aufbewahrungs- und Verjährungspflicht. Rufen Sie selten verwendete Clerk-Daten bei Bedarf ab, anstatt eine gesamte Nutzlast zu duplizieren.

Machen Sie Wiederholungsversuche und Bestellungen unschädlich

Clerk dokumentiert, dass eine Nicht-2xx-Antwort einen erneuten Ereignisversuch auslöst. Erfassen Sie die Webhook-Nachrichtenkennung aus den signierten Webhook-Metadaten oder -Headern als eindeutige Quittung, bevor Sie Effekte anwenden. Wenn Ihr SDK Standard-Webhooks-IDs in Headern bereitstellt, behalten Sie diese zusammen mit dem Ereignistyp und dem Zeitstempel bei. Geben Sie 200 für ein fertiges Duplikat zurück.

Vergleichen Sie für Benutzeraktualisierungen Ereigniszeitstempel oder verwenden Sie Last-Write-Regeln, die verhindern, dass ein älteres Ereignis neuere Profildaten überschreibt. Rufen Sie für einen Status mit hohem Wert den aktuellen Benutzer nach der Überprüfung von Clerk ab und behandeln Sie den Webhook als Signal zum Abgleich. Behalten Sie die Quittungseinfügung, die Benutzeraktualisierung und den Postausgangsauftrag in einer Datenbanktransaktion bei.

await db.transaction(async (tx) => {
  const inserted = await tx.webhookReceipt.insertIfAbsent(messageId);
  if (!inserted) return;

  await tx.user.upsert({
    clerkUserId: clerk.id,
    primaryEmail: findPrimaryEmail(clerk),
    sourceUpdatedAt: eventTimestamp,
  });
  await tx.outbox.enqueue('profile-synced', { clerkUserId: clerk.id });
});

Dieses Muster verhindert doppelte Willkommens-E-Mails und teilweise Schreibvorgänge. Sehen Webhook-Wiederholungsversuche und Idempotenz für Schemaoptionen.

Beheben Sie Fehler bei Lieferungen durch lokale Clerk

404, Redirect oder HTML anstelle Ihres Handlers

Überprüfen Sie den Dateipfad, den POST-Export und die vollständige Tunnel-URL. Überprüfen Sie die Middleware und das Umschreiben des Gebietsschemas. Ein Webhook sollte keine Anmeldeumleitung oder CSRF-Formularprüfung durchlaufen. Testen Sie die öffentliche URL mit einem einfachen POST; Erwarten Sie von Ihrer Route eine Signaturablehnung, kein Framework 404.

verifyWebhook() wirft immer

Starten Sie Next.js neu, nachdem Sie das Signaturgeheimnis festgelegt haben. Bestätigen Sie, dass das Geheimnis zu diesem Endpunkt und dieser Umgebung gehört. Analysieren, klonen Sie nicht falsch und verbrauchen Sie den Anforderungstext nicht, bevor Sie ihn an den Helfer übergeben. Stellen Sie sicher, dass der Tunnel die Standard-Webhooks-Signaturheader beibehält.

Das Dashboard zeigt Wiederholungsversuche an

Schauen Sie sich die genaue Versuchsantwort an. Geben Sie 2xx erst nach dauerhafter Annahme zurück, aber setzen Sie die Verarbeitung unterhalb des Provider-Timeouts fort. Datenbankmigrationen, Fehler bei der Eindeutigkeitsbeschränkung und der synchrone Aufruf eines nicht verfügbaren Dienstes sind häufige 500-Ursachen.

Zeilen sind dupliziert oder veraltet

Fügen Sie eindeutige Einschränkungen für die Clerk-ID und die Webhook-Nachrichten-ID hinzu. Machen user.created Und user.updated beide sicheren Upserts, dann Schutz vor älteren Ereigniszeitstempeln. Wiederholen Sie das Beispiel nach jedem Fix, um die Handhabung von Duplikaten zu beweisen.

Sicherheitscheckliste

  • Überprüfen Sie jede Anfrage mit dem Clerk-Helfer, bevor Sie Nutzlastfelder protokollieren oder Daten schreiben.
  • Lassen Sie die Route nicht durch Benutzersitzungs-Middleware authentifiziert, sondern durch die Webhook-Signatur schützen.
  • Verwenden Sie separate Endpunktgeheimnisse für lokale, Vorschau-, Staging- und Produktionsumgebungen.
  • Deduplizieren Sie signierte Nachrichten-IDs und beschränken Sie Benutzer-IDs eindeutig.
  • Schwärzen Sie E-Mails, Telefon, Token, Geheimnisse und die gesamte Nutzlast aus normalen Protokollen.
  • Fügen Sie optional von Clerk/Svix dokumentierte IP-Kontrollen als Tiefenverteidigung hinzu, ersetzen Sie jedoch niemals die Signaturüberprüfung durch IP-Filterung.
  • Begrenzen Sie die Rate ungültigen Datenverkehrs, begrenzen Sie die Körpergröße und rotieren Sie das Geheimnis, wenn es in Protokollen oder in der Quellcodeverwaltung erscheint.

Clerk Webhooks-Übersicht erklärt die Signaturüberprüfung und optionale Svix-IP-Einschränkungen. Fahren Sie für die Rohkörper-Grundlagen und das Routenverhalten von Next.j mit dem fort Next.js Localhost-Webhook-Anleitung.

Häufig gestellte Fragen

Wie teste ich einen Clerk-Webhook lokal mit Next.js?
Erstellen Sie eine App-Router-POST-Route, veröffentlichen Sie Port 3000 über einen HTTPS-Tunnel, tragen Sie die vollständige öffentliche Route im Clerk Dashboard ein, setzen Sie CLERK_WEBHOOK_SIGNING_SECRET und senden Sie im Test-Tab des Endpunkts ein Beispiel.
Sollte clerkMiddleware die Clerk-Webhook-Route schützen?
Die Route muss ohne Benutzersitzung erreichbar sein; führen Sie dort daher kein auth.protect() aus. Authentifizieren Sie die Maschinenanfrage stattdessen mit verifyWebhook() und dem Signaturgeheimnis des Endpunkts.
Muss ich den Raw Body vor Clerk verifyWebhook() parsen?
Nein. Übergeben Sie die ursprüngliche Request direkt an verifyWebhook(). Rufen Sie vorher weder request.json() noch request.text() auf, da der Helper den signierten Request-Body und die Header benötigt.
Wie behandle ich Wiederholungen von Clerk user.created?
Führen Sie anhand der eindeutigen Clerk-Benutzer-ID ein Upsert aus, deduplizieren Sie über die ID der signierten Webhook-Nachricht und geben Sie für bereits verarbeitete Ereignisse 200 zurück. Stellen Sie nicht notwendige Seiteneffekte in eine Queue, um doppelte E-Mails zu verhindern.