Todos los artículos
Eventos del ciclo de vida de usuarios de Clerk que atraviesan un túnel de webhooks firmado hasta un endpoint de Next.js App Router y una base de datos local.
ClerkNext.jswebhooksuser sync

Cómo probar webhooks de Clerk en localhost con Next.js

Para probar los webhooks de Clerk en localhost en Next.js, cree una ruta POST de App Router, exponga el puerto 3000 con un túnel HTTPS, agregue la URL pública como punto final del webhook de Clerk y verifique cada solicitud con verifyWebhook() antes de sincronizar los datos del usuario. Mantenga la ruta pública en el middleware Clerk: el secreto de firma autentica la solicitud de máquina a máquina, no una sesión de navegador.

Cuando los webhooks de Clerk son la herramienta de sincronización adecuada

Clerk sigue siendo la fuente de identidad de la verdad. Un webhook es útil cuando su aplicación necesita una proyección local para uniones, búsquedas, informes, metadatos de autorización o integraciones que no pueden consultar a Clerk a pedido. Los eventos típicos incluyen user.created, user.updated, y user.deleted.

Un webhook es asincrónico. El usuario puede finalizar el registro antes de que exista la proyección de su base de datos, las entregas se pueden volver a intentar y las actualizaciones pueden llegar juntas. No haga de la proyección su única fuente para realizar verificaciones de identidad inmediatas posteriores al registro. Diseñe lecturas para tolerar un breve retraso o cree la fila de la aplicación explícitamente en el flujo de usuarios y permita que los webhooks la concilien.

Crear un punto final público de Next.js App Router

Agregar app/api/webhooks/clerk/route.ts. actual de Clerk guía de sincronización usos verifyWebhook de @clerk/nextjs/webhooks. El asistente consume la solicitud, verifica la firma de los webhooks estándar y devuelve datos del evento escritos.

// 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 });
  }
}

Por defecto, el ayudante lee CLERK_WEBHOOK_SIGNING_SECRET. secretaria verificarReferencia de Webhook también permite una explícita signingSecret opción, pero la configuración del entorno evita incrustar el secreto en el código fuente.

Excluir la ruta del webhook de la protección de la sesión

Las solicitudes de webhook no transportan la sesión de Clerk de su usuario. Si el middleware llama auth.protect() Para cada ruta de API, la entrega de Clerk recibe una redirección, 401 o 404 antes de que se ejecute la verificación de firma. Defina rutas de aplicaciones protegidas explícitamente y abandone /api/webhooks/clerk público.

// 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)(.*)',
  ],
};

secretaria guía de depuración de webhooks menciona específicamente la exclusión de rutas de webhook. En las versiones más nuevas de Next.js, la convención puede usar proxy.ts; siga la guía de la versión Clerk instalada en su proyecto. Público no significa confiable: verifyWebhook() Es obligatorio antes de cualquier acción.

Conecte el empleado al host local

  1. Correr npm run dev y verifique que la aplicación Next.js esté escuchando en el puerto 3000.
  2. Correr npx portpreview 3000.
  3. En el Panel del empleado, cree un punto final de webhook con https://your-subdomain.portpreview.dev/api/webhooks/clerk.
  4. Seleccione solo el usuario, sesión, organización o eventos de correo electrónico requeridos.
  5. Copie el secreto de firma del punto final en CLERK_WEBHOOK_SIGNING_SECRET en su entorno local y reinicie Next.js.
  6. Abra la pestaña Pruebas del endpoint, elija user.createdy seleccione Enviar ejemplo.
  7. Confirme que el intento dice Exitoso, su ruta local devolvió 200 y la fila esperada de la base de datos cambió una vez.

La URL del túnel debe permanecer activa para ejemplos posteriores. Si cambia, actualice el punto final. No reutilice el secreto de firma de un punto final de producción para pruebas locales; cree puntos finales específicos del entorno para que la rotación y el historial de auditoría permanezcan claros.

Modele la sincronización de usuarios con cuidado

Utilice el ID de usuario del cajero como clave externa

Almacenar user_... en un único clerk_user_id columna. Inserte esa clave para volver a intentarlo. user.created converge en lugar de fallar. Mantenga su propia clave primaria interna si otras tablas ya hacen referencia a ella.

Elija el correo electrónico principal deliberadamente

Los datos del usuario de Clerk contienen registros de direcciones de correo electrónico y una ID de dirección de correo electrónico principal. Resuelva el registro principal por ID en lugar de tomar el primer elemento de la matriz. El correo electrónico puede cambiar; no lo utilice como clave externa inmutable.

Decidir qué significa eliminar

A user.deleted El evento puede contener menos datos que los creados o actualizados. Utilice el ID para anonimizar, eliminar temporalmente o iniciar un flujo de trabajo de retención de acuerdo con su política. La eliminación ciega en cascada puede destruir registros de facturación o auditoría que las normas exigen conservar.

No reflejes todo

Persista solo los campos que su aplicación necesita. Cada campo de perfil copiado crea una obligación de privacidad, retención y obsolescencia. Obtenga datos de Clerk que rara vez se utilizan a pedido en lugar de duplicar una carga útil completa.

Haga que los reintentos y los pedidos sean inofensivos

El Clerk documenta que una respuesta que no sea 2xx provoca un reintento del evento. Registre el identificador del mensaje del webhook a partir de los metadatos o encabezados del webhook firmados como un recibo único antes de aplicar los efectos. Si su SDK expone ID de Webhooks estándar en los encabezados, consérvelos junto con el tipo de evento y la marca de tiempo. Devuelva 200 para obtener un duplicado completo.

Para las actualizaciones de los usuarios, compare las marcas de tiempo de los eventos o utilice reglas de última escritura que impidan que un evento anterior sobrescriba los datos del perfil más nuevos. Para un estado de alto valor, recupere el usuario actual de Clerk después de la verificación y trate el webhook como una señal para conciliar. Mantenga la inserción del recibo, la actualización del usuario y el trabajo de la bandeja de salida en una transacción de base de datos.

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 });
});

Este patrón evita correos electrónicos de bienvenida duplicados y escrituras parciales. Ver Reintentos de webhook e idempotencia. para opciones de esquema.

Solucionar problemas de entregas de empleados locales

404, redireccionamiento o HTML en lugar de su controlador

Verifique la ruta del archivo, la exportación POST y la URL del túnel completo. Verifique las reescrituras de middleware y configuración regional. Un webhook no debe pasar por una redirección de inicio de sesión o una verificación de formulario CSRF. Pruebe la URL pública con un POST básico; espere el rechazo de la firma de su ruta, no un marco 404.

verifyWebhook() siempre tira

Reinicie Next.js después de configurar el secreto de firma. Confirme que el secreto pertenece a este punto final y entorno. No analice, clone incorrectamente ni consuma el cuerpo de la solicitud antes de pasarlo al asistente. Asegúrese de que el túnel conserve los encabezados de firma de los Webhooks estándar.

El panel muestra reintentos

Mire la respuesta del intento exacto. Devuelva 2xx solo después de una aceptación duradera, pero continúe el procesamiento por debajo del tiempo de espera del proveedor. Las migraciones de bases de datos, los errores de restricciones únicas y la llamada sincrónica a un servicio no disponible son 500 causas comunes.

Las filas están duplicadas o obsoletas

Agregue restricciones únicas para el ID del cajero y el ID del mensaje del webhook. Hacer user.created y user.updated ambas inserciones seguras, luego protéjase contra marcas de tiempo de eventos más antiguas. Vuelva a reproducir el ejemplo después de cada corrección para demostrar el manejo duplicado.

Lista de verificación de seguridad

  • Verifique cada solicitud con el ayudante de Clerk antes de registrar campos de carga útil o escribir datos.
  • Mantenga la ruta sin autenticar mediante el middleware de sesión de usuario, pero protegida por la firma del webhook.
  • Utilice secretos de punto final independientes para entornos locales, de vista previa, de ensayo y de producción.
  • Deduplica los ID de mensajes firmados y restringe los ID de usuario de forma única.
  • Redactar correos electrónicos, teléfonos, tokens, secretos y cargas útiles completas de los registros normales.
  • Opcionalmente, agregue controles de IP documentados por Clerk/Svix como defensa en profundidad, pero nunca reemplace la verificación de firma con filtrado de IP.
  • Limite el tráfico no válido, limite el tamaño del cuerpo y rote el secreto si aparece en los registros o en el control de fuente.

secretaria descripción general de webhooks explica la verificación de firma y las restricciones opcionales de IP de Svix. Para conocer los fundamentos del cuerpo sin formato y el comportamiento de ruta de Next.js, continúe con el Guía del webhook localhost de Next.js.

Preguntas frecuentes

¿Cómo pruebo un webhook de Clerk en local con Next.js?
Crea una ruta POST de App Router, expón el puerto 3000 con un túnel HTTPS, añade la ruta pública completa en Clerk Dashboard, configura CLERK_WEBHOOK_SIGNING_SECRET y envía un ejemplo desde la pestaña de pruebas del endpoint.
¿Debe clerkMiddleware proteger la ruta del webhook de Clerk?
Debe ser accesible sin una sesión de usuario, así que no ejecutes auth.protect() en ella. Autentica la solicitud entre máquinas con verifyWebhook() y el secreto de firma del endpoint.
¿Debo analizar el cuerpo sin procesar antes de Clerk verifyWebhook()?
No. Pasa la Request original directamente a verifyWebhook(). No llames antes a request.json() ni request.text(), porque el helper necesita el cuerpo firmado y los encabezados.
¿Cómo se gestionan los reintentos de user.created de Clerk?
Haz un upsert por el ID de usuario único de Clerk, elimina duplicados por el ID del mensaje webhook firmado y devuelve 200 si el evento ya fue procesado. Pon en cola los efectos secundarios no esenciales para evitar correos duplicados.