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
- Correr
npm run devy verifique que la aplicación Next.js esté escuchando en el puerto 3000. - Correr
npx portpreview 3000. - En el Panel del empleado, cree un punto final de webhook con
https://your-subdomain.portpreview.dev/api/webhooks/clerk. - Seleccione solo el usuario, sesión, organización o eventos de correo electrónico requeridos.
- Copie el secreto de firma del punto final en
CLERK_WEBHOOK_SIGNING_SECRETen su entorno local y reinicie Next.js. - Abra la pestaña Pruebas del endpoint, elija
user.createdy seleccione Enviar ejemplo. - 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.
