Tất cả bài viết
Các sự kiện vòng đời người dùng Clerk đi qua đường hầm webhook có chữ ký đến endpoint Next.js App Router và cơ sở dữ liệu cục bộ.
ClerkNext.jswebhooksuser sync

Kiểm thử webhook Clerk trên localhost với Next.js

Để kiểm tra webhook của Clerk trên localhost trong Next.js, hãy tạo tuyến POST của Bộ định tuyến ứng dụng, hiển thị cổng 3000 bằng đường hầm HTTPS, thêm URL công khai làm điểm cuối webhook của Clerk và xác minh từng yêu cầu bằng verifyWebhook() trước khi đồng bộ hóa dữ liệu người dùng. Giữ tuyến đường công khai trong phần mềm trung gian Clerk: bí mật ký xác thực yêu cầu giữa các máy chứ không phải phiên trình duyệt.

Khi webhook của Clerk là công cụ đồng bộ phù hợp

Clerk vẫn là nguồn gốc của sự thật. Webhook rất hữu ích khi ứng dụng của bạn cần một phép chiếu cục bộ để tham gia, tìm kiếm, báo cáo, siêu dữ liệu ủy quyền hoặc tích hợp không thể truy vấn Clerk theo yêu cầu. Các sự kiện điển hình bao gồm user.created, user.updated, Và user.deleted.

Webhook không đồng bộ. Người dùng có thể hoàn tất đăng ký trước khi dự báo cơ sở dữ liệu của bạn tồn tại, việc phân phối có thể được thử lại và các bản cập nhật có thể đến gần nhau. Đừng coi dự báo là nguồn duy nhất của bạn để kiểm tra danh tính ngay sau khi đăng ký. Thiết kế đọc để chấp nhận độ trễ ngắn hoặc tạo hàng ứng dụng một cách rõ ràng trong luồng người dùng và để webhooks điều chỉnh nó.

Tạo điểm cuối Bộ định tuyến ứng dụng Next.js công khai

Thêm vào app/api/webhooks/clerk/route.ts. Clerk hiện tại hướng dẫn đồng bộ công dụng verifyWebhook từ @clerk/nextjs/webhooks. Trình trợ giúp tiếp nhận Yêu cầu, kiểm tra chữ ký Webhooks tiêu chuẩn và trả về dữ liệu sự kiện đã nhập.

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

Theo mặc định, người trợ giúp sẽ đọc CLERK_WEBHOOK_SIGNING_SECRET. Clerk xác minh tham chiếu Webhook cũng cho phép một cách rõ ràng signingSecret tùy chọn, nhưng cấu hình môi trường tránh nhúng bí mật vào nguồn.

Loại trừ tuyến webhook khỏi bảo vệ phiên

Yêu cầu Webhook không mang theo phiên Clerk của người dùng. Nếu phần mềm trung gian gọi auth.protect() đối với mọi đường dẫn API, việc gửi của Clerk sẽ nhận được chuyển hướng, 401 hoặc 404 trước khi chạy xác minh chữ ký. Xác định rõ ràng các tuyến ứng dụng được bảo vệ và để lại /api/webhooks/clerk công cộng.

// 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 hướng dẫn gỡ lỗi webhook đặc biệt gọi ra ngoại trừ các tuyến webhook. Trong các phiên bản Next.js mới hơn, quy ước có thể sử dụng proxy.ts; làm theo hướng dẫn phiên bản Clerk được cài đặt trong dự án của bạn. Công khai không có nghĩa là đáng tin cậy: verifyWebhook() là bắt buộc trước bất kỳ hành động nào.

Kết nối Clerk với localhost

  1. Chạy npm run dev và xác minh ứng dụng Next.js đang lắng nghe trên cổng 3000.
  2. Chạy npx portpreview 3000.
  3. Trong Bảng điều khiển Clerk, tạo điểm cuối webhook với https://your-subdomain.portpreview.dev/api/webhooks/clerk.
  4. Chỉ chọn sự kiện người dùng, phiên, tổ chức hoặc email được yêu cầu.
  5. Sao chép Bí mật ký kết điểm cuối vào CLERK_WEBHOOK_SIGNING_SECRET trong môi trường cục bộ của bạn và khởi động lại Next.js.
  6. Mở tab Kiểm tra của điểm cuối, chọn user.createdvà chọn Gửi ví dụ.
  7. Xác nhận lần thử cho biết Đã thành công, tuyến đường địa phương của bạn trả về 200 và hàng cơ sở dữ liệu dự kiến ​​đã thay đổi một lần.

URL đường hầm phải luôn hoạt động cho các ví dụ tiếp theo. Nếu nó thay đổi, hãy cập nhật điểm cuối. Không sử dụng lại bí mật ký kết của điểm cuối sản xuất cho các thử nghiệm cục bộ; tạo các điểm cuối dành riêng cho môi trường để lịch sử luân chuyển và kiểm tra vẫn rõ ràng.

Đồng bộ hóa người dùng mô hình một cách cẩn thận

Sử dụng ID người dùng Clerk làm khóa bên ngoài

Cửa hàng user_... một cách độc đáo clerk_user_id cột. Upsert trên phím đó để thử lại user.created hội tụ thay vì thất bại. Giữ khóa chính nội bộ của riêng bạn nếu các bảng khác đã tham chiếu nó.

Chọn email chính một cách có chủ ý

Dữ liệu người dùng Clerk chứa bản ghi địa chỉ email và ID địa chỉ email chính. Giải quyết bản ghi chính bằng ID thay vì lấy phần tử mảng đầu tiên. Email có thể thay đổi; không sử dụng nó làm khóa ngoại bất biến.

Quyết định ý nghĩa của việc xóa

MỘT user.deleted sự kiện có thể chứa ít dữ liệu hơn sự kiện tạo hoặc cập nhật. Sử dụng ID để ẩn danh, xóa tạm thời hoặc bắt đầu quy trình lưu giữ theo chính sách của bạn. Xóa tầng mù có thể phá hủy hồ sơ thanh toán hoặc kiểm tra mà các quy định yêu cầu bạn phải lưu giữ.

Đừng phản chiếu mọi thứ

Chỉ duy trì các trường mà ứng dụng của bạn cần. Mỗi trường hồ sơ được sao chép sẽ tạo ra nghĩa vụ về quyền riêng tư, lưu giữ và độ ổn định. Tìm nạp hiếm khi sử dụng dữ liệu Clerk theo yêu cầu thay vì sao chép toàn bộ tải trọng.

Làm cho việc thử lại và đặt hàng trở nên vô hại

Clerk ghi lại rằng phản hồi không phải 2xx gây ra sự kiện thử lại. Ghi lại mã nhận dạng tin nhắn webhook từ siêu dữ liệu hoặc tiêu đề webhook đã ký dưới dạng biên nhận duy nhất trước khi áp dụng hiệu ứng. Nếu SDK của bạn hiển thị ID Webhooks tiêu chuẩn trong tiêu đề, hãy giữ chúng cùng với loại sự kiện và dấu thời gian. Trả về 200 cho một bản sao hoàn chỉnh.

Để cập nhật cho người dùng, hãy so sánh dấu thời gian của sự kiện hoặc sử dụng quy tắc ghi lần cuối để ngăn sự kiện cũ ghi đè dữ liệu hồ sơ mới hơn. Đối với trạng thái có giá trị cao, hãy truy xuất người dùng hiện tại từ Clerk sau khi xác minh và coi webhook là tín hiệu để điều chỉnh. Giữ công việc chèn biên lai, cập nhật người dùng và hộp thư đi trong một giao dịch cơ sở dữ liệu.

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

Mẫu này ngăn chặn các email chào mừng trùng lặp và ghi một phần. Nhìn thấy thử lại webhook và idempotency cho các tùy chọn lược đồ.

Khắc phục sự cố giao hàng của Clerk địa phương

404, chuyển hướng hoặc HTML thay vì trình xử lý của bạn

Xác minh đường dẫn tệp, xuất POST và URL đường hầm đầy đủ. Kiểm tra phần mềm trung gian và viết lại miền địa phương. Webhook không được chuyển hướng đăng nhập hoặc kiểm tra biểu mẫu CSRF. Kiểm tra URL công khai bằng POST cơ bản; mong đợi sự từ chối chữ ký từ tuyến đường của bạn chứ không phải khung 404.

verifyWebhook() luôn ném

Khởi động lại Next.js sau khi đặt bí mật ký. Xác nhận bí mật thuộc về điểm cuối và môi trường này. Không phân tích cú pháp, sao chép không chính xác hoặc sử dụng phần nội dung Yêu cầu trước khi chuyển nó cho người trợ giúp. Đảm bảo đường hầm duy trì tiêu đề chữ ký Webhooks tiêu chuẩn.

Trang tổng quan hiển thị số lần thử lại

Nhìn vào phản hồi nỗ lực chính xác. Chỉ trả lại 2xx sau khi được chấp nhận lâu dài nhưng vẫn tiếp tục xử lý dưới thời gian chờ của nhà cung cấp. Di chuyển cơ sở dữ liệu, lỗi ràng buộc duy nhất và gọi đồng bộ một dịch vụ không khả dụng là 500 nguyên nhân phổ biến.

Hàng bị trùng lặp hoặc cũ

Thêm các ràng buộc duy nhất cho ID Clerk và ID tin nhắn webhook. Làm user.createduser.updated cả hai đều tăng cường an toàn, sau đó đề phòng các dấu thời gian sự kiện cũ hơn. Phát lại ví dụ sau mỗi lần sửa để chứng minh việc xử lý trùng lặp.

Danh sách kiểm tra bảo mật

  • Xác minh mọi yêu cầu với trình trợ giúp của Clerk trước khi ghi các trường tải trọng hoặc ghi dữ liệu.
  • Giữ tuyến đường không được xác thực bởi phần mềm trung gian phiên người dùng nhưng được bảo vệ bằng chữ ký webhook.
  • Sử dụng các bí mật điểm cuối riêng biệt cho môi trường cục bộ, xem trước, dàn dựng và sản xuất.
  • Loại bỏ ID tin nhắn đã ký trùng lặp và hạn chế ID người dùng duy nhất.
  • Biên tập lại email, điện thoại, mã thông báo, bí mật và toàn bộ tải trọng từ nhật ký thông thường.
  • Tùy chọn thêm các biện pháp kiểm soát IP được ghi lại của Clerk/Svix để bảo vệ chuyên sâu nhưng không bao giờ thay thế xác minh chữ ký bằng lọc IP.
  • Giới hạn tỷ lệ lưu lượng truy cập không hợp lệ, giới hạn kích thước nội dung và xoay vòng bí mật nếu nó xuất hiện trong nhật ký hoặc kiểm soát nguồn.

Clerk tổng quan về webhook giải thích xác minh chữ ký và các hạn chế IP Svix tùy chọn. Đối với các nguyên tắc cơ bản về cơ thể thô và hành vi tuyến đường của Next.js, hãy tiếp tục với Hướng dẫn webhook localhost Next.js.

Câu hỏi thường gặp

Làm cách nào để kiểm thử webhook Clerk cục bộ trong Next.js?
Tạo route POST của App Router, công khai cổng 3000 qua đường hầm HTTPS, thêm đầy đủ route công khai trong Clerk Dashboard, đặt CLERK_WEBHOOK_SIGNING_SECRET rồi gửi ví dụ từ tab kiểm thử của endpoint.
Có nên bảo vệ route webhook Clerk bằng clerkMiddleware không?
Route phải truy cập được khi không có phiên người dùng, vì vậy không chạy auth.protect() trên route đó. Thay vào đó, hãy xác thực yêu cầu giữa các máy bằng verifyWebhook() và secret ký của endpoint.
Có cần parse raw body trước Clerk verifyWebhook() không?
Không. Hãy truyền Request ban đầu trực tiếp vào verifyWebhook(). Không gọi request.json() hoặc request.text() trước vì helper cần body và headers đã được ký.
Nên xử lý các lần gửi lại Clerk user.created như thế nào?
Upsert theo ID người dùng Clerk duy nhất, loại bỏ trùng lặp theo ID thông điệp webhook đã ký và trả về 200 cho sự kiện đã xử lý. Đưa các side effect không thiết yếu vào hàng đợi để tránh gửi email trùng lặp.