Tất cả bài viết
Các phong bì sự kiện email đã gửi, bị trả lại và bị khiếu nại đi qua đường hầm có chữ ký vào route Next.js trên localhost.
ResendNext.jsemail webhookslocalhost

Kiểm thử webhook Resend cục bộ với Next.js

Để kiểm tra cục bộ webhook Resend trong Next.js, hãy tạo tuyến App Router POST để đọc nội dung thô, xác minh tiêu đề Svix của nó bằng bí mật ký Resend của bạn và đăng ký URL đường hầm HTTPS trong bảng điều khiển Resend. Gửi email qua Resend, sau đó xử lý thật email.sent, email.delivered, email.bouncedhoặc email.complained sự kiện trên localhost.

Webhook Resend cho ứng dụng của bạn biết điều gì

Phản hồi API cho biết email đã được chấp nhận không phải là bằng chứng cho thấy email đó đã đến tay người nhận. Quá trình giao hàng diễn ra không đồng bộ. Webhooks Resend cho phép ứng dụng của bạn cập nhật trạng thái thông báo, loại bỏ các địa chỉ xấu, báo cáo số lần gửi bị trả lại và phản hồi các khiếu nại sau khi yêu cầu gửi ban đầu kết thúc. các tài liệu webhook Resend chính thức liệt kê các loại sự kiện và thiết lập trang tổng quan.

Kiểm tra webhook cục bộ sẽ bao gồm toàn bộ máy trạng thái, không chỉ liệu POST có đến được tuyến đường của bạn hay không. Đối chiếu ID email của từng sự kiện với bản ghi được tạo khi gửi. Xử lý các trạng thái dưới dạng chuyển tiếp: được chấp nhận, đã gửi, đã gửi, bị trì hoãn, bị trả lại, bị khiếu nại, đã mở hoặc được nhấp vào nếu có. Bản sao sau này không được ghi đè trạng thái hữu ích hơn hoặc kích hoạt cùng một cảnh báo hai lần.

Tạo tuyến đường Next.js App Router

Cài đặt trình xác minh được duy trì cho định dạng ký:

npm install svix

Sau đó tạo tuyến đường thời gian chạy Node. Resend ký vào phần thân gốc nên hãy sử dụng request.text() đúng một lần trước khi phân tích cú pháp.

// app/api/webhooks/resend/route.ts
import { Webhook } from 'svix';

export const runtime = 'nodejs';

export async function POST(request: Request) {
  const payload = await request.text();
  const headers = {
    'svix-id': request.headers.get('svix-id') ?? '',
    'svix-timestamp': request.headers.get('svix-timestamp') ?? '',
    'svix-signature': request.headers.get('svix-signature') ?? '',
  };

  let event: ResendEvent;
  try {
    const webhook = new Webhook(process.env.RESEND_WEBHOOK_SECRET!);
    event = webhook.verify(payload, headers) as ResendEvent;
  } catch {
    return Response.json({ error: 'Invalid webhook signature' }, { status: 400 });
  }

  await enqueueResendEvent({
    deliveryId: headers['svix-id'],
    event,
  });
  return Response.json({ received: true });
}

Bí mật ký thuộc về điểm cuối webhook này và thường bắt đầu bằng tiền tố dành riêng cho nhà cung cấp. Sao chép nó từ cài đặt webhook Resend vào một tệp môi trường cục bộ bị bỏ qua, chẳng hạn như .env.local. Đây không phải là khóa API Resend được sử dụng để gửi email.

Tại sao ba tiêu đề Svix lại quan trọng

  • svix-id xác định duy nhất một lần giao hàng và là khóa tạm thời tốt nhất.
  • svix-timestamp liên kết chữ ký với một thời điểm, cho phép người xác minh từ chối các yêu cầu cũ ngoài khả năng chịu đựng của nó.
  • svix-signature có thể chứa một hoặc nhiều chữ ký được phiên bản được sử dụng để xác thực nội dung.

Không triển khai giao thức này bằng cách tách chuỗi tiêu đề trừ khi bạn có lý do thuyết phục. SDK xử lý mã hóa, nhiều chữ ký và kiểm tra dấu thời gian. Resend khuyến nghị rõ ràng việc sử dụng bí mật ký và các tiêu đề này để xác minh. Càng sâu hướng dẫn xác minh chữ ký giải thích tại sao byte thô và kiểm tra an toàn về thời gian lại quan trọng.

Hiển thị Next.js và đăng ký điểm cuối

  1. Chạy npm run dev và xác nhận ứng dụng lắng nghe trên cổng 3000.
  2. Bắt đầu npx portpreview 3000 trong một thiết bị đầu cuối khác.
  3. Trong Resend, tạo một webhook có điểm cuối là https://YOUR-TUNNEL.portpreview.dev/api/webhooks/resend.
  4. Chỉ chọn các sự kiện email mà ứng dụng của bạn xử lý.
  5. Sao chép bí mật ký của điểm cuối vào RESEND_WEBHOOK_SECRET và khởi động lại Next.js để nó tải biến.
  6. Gửi tin nhắn bằng miền đã được xác minh và kiểm tra các sự kiện tiếp cận tuyến đường địa phương.

Giữ URL công khai ổn định cho phiên. Nếu gốc đường hầm thay đổi, hãy chỉnh sửa điểm cuối Resend trước khi kiểm tra lại. Điểm cuối được định cấu hình bằng URL cũ không thể tiếp cận quy trình mới của bạn, ngay cả khi bản thân localhost vẫn hoạt động tốt.

Sử dụng một bộ điều phối sự kiện đã gõ

Tải trọng webhook phải nhập vào một bộ điều phối hẹp. Xác thực các trường bắt buộc và làm cho các loại sự kiện không được nhận dạng có thể quan sát được mà không coi chúng là lỗi máy chủ.

async function processEvent(event: ResendEvent) {
  switch (event.type) {
    case 'email.delivered':
      await markDelivered(event.data.email_id, event.created_at);
      break;
    case 'email.bounced':
      await markBounced(event.data.email_id, event.data.bounce?.message);
      await suppressIfPermanent(event.data);
      break;
    case 'email.complained':
      await suppressRecipients(event.data.to);
      await alertCompliance(event.data.email_id);
      break;
    default:
      await recordUnhandledEvent(event);
  }
}

Giữ các loại tải trọng phù hợp với lược đồ Resend hiện tại thay vì giả sử mọi sự kiện đều có dữ liệu giống hệt nhau. Ví dụ: chi tiết bị trả lại và danh sách người nhận có thể chỉ liên quan đến một số sự kiện nhất định. Lưu loại sự kiện, ID email của nhà cung cấp, dấu thời gian sự kiện và trọng tải tối thiểu được điều chỉnh lại để điều tra hỗ trợ.

Đảm bảo xử lý có tính lũy đẳng trước khi thử nghiệm retry

Hệ thống Webhook cung cấp hành vi phân phối ít nhất một lần trong thực tế. Thời gian chờ có thể xảy ra sau khi cơ sở dữ liệu của bạn cam kết nhưng trước khi nhà cung cấp nhận được phản hồi 200 của bạn. Sau đó, nhà cung cấp sẽ thử lại yêu cầu mà bạn đã áp dụng. sử dụng svix-id làm khóa phân phối duy nhất và chèn nó vào cùng giao dịch khi trạng thái thay đổi.

await db.transaction(async (tx) => {
  const inserted = await tx.webhookDelivery.insertOnce({
    provider: 'resend',
    deliveryId,
  });
  if (!inserted) return;
  await applyEmailEvent(tx, event);
});

Không chỉ chống trùng lặp dựa trên ID email vì một email nhận được nhiều loại sự kiện một cách hợp pháp. Tùy thuộc vào mô hình dữ liệu của bạn, hãy giữ cả quy tắc khóa duy nhất ở cấp độ phân phối và quy tắc chuyển đổi trạng thái. Đọc các mẫu retry và bảo đảm tính lũy đẳng cho webhook trước khi kết nối các sự kiện với việc thanh toán, ngăn chặn hoặc thông báo cho khách hàng.

Trở về nhanh chóng mà không bị mất sự kiện

Xác minh chữ ký phù hợp trong đường dẫn yêu cầu; công việc kinh doanh chậm thì không. Duy trì hoặc xếp hàng sự kiện đã được xác minh, sau đó trả về 2xx. Nếu bạn quay lại trước bất kỳ thao tác ghi lâu dài nào, sự cố quy trình có thể làm mất sự kiện. Nếu bạn đợi một số API từ xa, điểm cuối của bạn có thể hết thời gian chờ và mời thử lại. Bảng hộp thư đến cơ sở dữ liệu thường là thiết kế sản xuất và cục bộ đơn giản nhất.

Tạo các sự kiện thử nghiệm hữu ích

Đã gửi và giao

Gửi tới địa chỉ mà bạn kiểm soát từ một miền đã được xác minh. Ghi lại ID email được API gửi trả về và xác nhận các sự kiện đến cập nhật cùng một hàng. Thời gian gửi khác nhau tùy theo máy chủ người nhận, vì vậy đừng cho rằng các sự kiện đến ngay lập tức hoặc theo một trình tự đơn giản.

Số lần trả lại

Sử dụng các địa chỉ kiểm tra hoặc tính năng kiểm tra được ghi lại của Resend thay vì tạo ra lưu lượng truy cập đến các tên miền không liên quan. Xác minh rằng các lỗi vĩnh viễn sẽ ngăn chặn thư trong tương lai trong khi các điều kiện tạm thời tuân theo chính sách thử lại của bạn. Không tự động ngăn chặn mọi sự kiện bị trì hoãn.

Khiếu nại

Xử lý khiếu nại vừa là khả năng cung cấp vừa là logic tuân thủ. Đảm bảo webhook lặp lại không tạo cảnh báo lặp lại và đảm bảo người nhận bị ảnh hưởng sẽ bị loại khỏi các chiến dịch sau này theo chính sách của bạn.

Khắc phục sự cố lỗi webhook Resend

Xác minh chữ ký luôn thất bại

Xác nhận bí mật ký của điểm cuối—không phải khóa API—đã được tải. sử dụng await request.text(), không phân tích cú pháp và sắp xếp lại chuỗi JSON và chuyển cả ba tiêu đề Svix với các giá trị chính xác của chúng. Khởi động lại máy chủ phát triển sau khi thay đổi .env.local.

Tuyến đường trả về 404 hoặc 405

Các tệp tuyến đường App Router phải được đặt tên route.ts bên dưới các phân đoạn URL dự định và xuất POST. Kiểm tra xem phần mềm trung gian có ghi lại yêu cầu đường hầm tới trang miền địa phương hoặc trang đăng nhập hay không. Kiểm tra URL công khai bằng tính năng cuộn tròn và kiểm tra phản hồi thực tế.

Resend hiển thị số lần thử lại mặc dù đã xử lý thành công

Kiểm tra xem mọi nhánh thành công có trả về 2xx kịp thời hay không. Các lỗi xuất hiện sau khi cập nhật cơ sở dữ liệu có thể tạo ra lỗi 500 và thử lại trùng lặp. Thực hiện xử lý giao dịch và bình thường, sau đó kiểm tra độ trễ phản hồi.

Sự kiện đến nhưng không thể liên kết với email

Giữ nguyên ID email của nhà cung cấp từ phản hồi gửi Resend ban đầu. Đừng dựa vào dòng chủ đề hoặc địa chỉ người nhận làm thông tin nhận dạng. Những trường đó không phải là duy nhất và cũng không đủ ổn định để tương quan.

Ảnh chụp lại không xác minh được dấu thời gian

Điều đó được mong đợi khi phát lại một yêu cầu đã ký cũ thông qua trình xác minh thông thường: dấu thời gian của nó có thể nằm ngoài dung sai cho phép. Ưu tiên nhà cung cấp giao lại nếu có. Đối với các thử nghiệm logic nghiệp vụ riêng biệt, hãy xác minh một lần, lưu lịch cố định sự kiện đã được dọn dẹp và kiểm tra riêng bộ điều phối. các hướng dẫn phát lại giải thích ranh giới này.

Bảo mật và quyền riêng tư để kiểm tra sự kiện email

  • Không bao giờ phơi bày RESEND_API_KEY hoặc bí mật ký điểm cuối trong nguồn, gói trình duyệt, ảnh chụp màn hình hoặc nhật ký yêu cầu.
  • Xác minh trước khi phân tích cú pháp hoặc duy trì sự kiện.
  • Biên tập lại người nhận, chủ đề, tiêu đề và siêu dữ liệu thư từ các ảnh chụp đường hầm được chia sẻ.
  • Áp dụng giới hạn lưu giữ cho tải trọng webhook thô; chỉ lưu trữ những gì được yêu cầu hỗ trợ và tuân thủ.
  • Sử dụng một bí mật điểm cuối cục bộ riêng biệt khỏi sản xuất và xoay nó khi điểm cuối kiểm tra bị xóa.

Thiết kế cuối cùng sẽ hoạt động giống hệt nhau sau khi triển khai: điểm cuối HTTPS công khai, xác minh nội dung thô, tính ổn định lâu bền, xác nhận nhanh và xử lý trạng thái không đồng bộ. Để biết chi tiết về thân máy thô dành riêng cho App Router, hãy xem Hướng dẫn localhost webhook Next.js.

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

Làm cách nào để kiểm tra webhooks Resend cục bộ trong Next.js?
Tạo tuyến đường POST App Router, xác minh nội dung thô bằng các tiêu đề Svix và bí mật ký điểm cuối, hiển thị cổng 3000 thông qua HTTPS và đăng ký URL công khai đó trong Resend.
Webhook Resend có nên sử dụng request.json() trong Next.js không?
Không phải trước khi xác minh. Đọc đang chờ request.text() để các byte đã ký không thay đổi, xác minh bằng Svix và sử dụng sự kiện đã xác minh do SDK trả về.
Bí mật ký webhook Resend có giống với khóa API không?
Không. Khóa API cho phép gửi yêu cầu. Mỗi điểm cuối webhook có một bí mật ký dùng để xác minh các sự kiện đến; lưu trữ cả hai một cách riêng biệt.
Làm cách nào để ngăn quá trình xử lý webhook Resend trùng lặp?
Lưu trữ Svix-id dưới một ràng buộc duy nhất và áp dụng sự kiện đó trong cùng một giao dịch. Không loại bỏ trùng lặp chỉ bằng ID email vì một email có nhiều sự kiện hợp lệ.