Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

受信者レコードを同期する

Email Sending のライフサイクルイベントに合わせて、アプリケーションの受信者レコードを同期します。

最終更新 Markdown で表示Agent セットアップ

Email Sending のイベントサブスクリプション を使い、配信の問題が発生したあとにアプリケーションのレコードを更新します。この例では Cloudflare QueuesWorkers KV を使い、トランザクション通知の対象から受信者を削除します。

リソースを準備する

開始する前に、次を用意します。

対象となる各受信者アドレスを、KV のキーとして保存します。値には通知設定や関連メタデータを含められます。

イベントの流れを確認する

  1. Email Sending がバウンスと苦情のイベントを発行する
  2. キューがそれらのイベントを Worker に配信する
  3. Worker が対象外の受信者レコードを KV から削除する

削除対象のイベントを選ぶ

すべての message.complained イベントでレコードを削除します。これらのイベントは、受信者がメッセージをスパムとして報告したことを示します。

バウンスのレコードは、payload.bounce.type"hard" のときだけ削除します。一時的な失敗は、再試行が残っているあいだ message.deferred イベントになります。一時的な再試行が尽きると、"soft" バウンス種別の message.bounced イベントになることがあります。

ペイロードの詳細は、利用可能な Email Sending イベント を参照してください。

キューとサブスクリプションを作成する

キューを作成し、送信ドメインにサブスクライブします。

  1. Cloudflare ダッシュボードで Queues ページを開きます。email-events という名前のキューを作成します。

    Queues を開く ↗
  2. email-events を選び、Subscriptions > Subscribe to events を選びます。

  3. サブスクリプション名を入力し、ソースとして Email Sending を選びます。

  4. 送信ドメインと、message.bounced および message.complained イベントを選びます。

  5. Subscribe を選びます。

Worker を設定する

KV 名前空間をバインドし、Worker をキューのコンシューマーとして登録します。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "recipient-record-sync",
  "main": "src/index.ts",
  // Set this to today's date
  "compatibility_date": "2026-09-20",
  "kv_namespaces": [
    {
      "binding": "RECIPIENTS",
      "id": "<RECIPIENTS_KV_NAMESPACE_ID>"
    }
  ],
  "queues": {
    "consumers": [
      {
        "queue": "email-events",
        "max_batch_size": 10,
        "max_retries": 3,
        "dead_letter_queue": "email-events-dlq"
      }
    ]
  }
}
name = "recipient-record-sync"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"

[[kv_namespaces]]
binding = "RECIPIENTS"
id = "<RECIPIENTS_KV_NAMESPACE_ID>"

[[queues.consumers]]
queue = "email-events"
max_batch_size = 10
max_retries = 3
dead_letter_queue = "email-events-dlq"

構成では、デプロイ時に email-events-dlq が作成されます。Queues は 3 回再試行したあと、イベントをそこに移します。

キューコンシューマーを追加する

queue() ハンドラー は各イベントを独立して処理します。対象の受信者レコードを削除し、失敗した KV 操作を再試行します。

src/index.jsjs
export default {
	async queue(batch, env) {
		for (const message of batch.messages) {
			try {
				const event = message.body;

				if (shouldRemove(event)) {
					await removeRecipient(env, event);
				}

				message.ack();
			} catch (error) {
				console.error("Failed to process Email Sending event", {
					eventId: message.body.payload.eventId,
					error,
				});
				message.retry();
			}
		}
	},
};

function shouldRemove(event) {
	if (event.type === "cf.email.sending.message.complained") {
		return true;
	}

	return (
		event.type === "cf.email.sending.message.bounced" &&
		event.payload.bounce?.type === "hard"
	);
}

async function removeRecipient(env, event) {
	await env.RECIPIENTS.delete(event.payload.recipient);
	console.log("Removed recipient record", {
		eventId: event.payload.eventId,
		reason: event.type,
	});
}
src/index.tsts
interface Env {
	RECIPIENTS: KVNamespace;
}

interface EmailSendingEvent {
	type:
		| "cf.email.sending.message.bounced"
		| "cf.email.sending.message.complained";
	payload: {
		eventId: string;
		recipient: string;
		bounce?: {
			type: "hard" | "soft";
		};
	};
}

export default {
	async queue(batch, env): Promise<void> {
		for (const message of batch.messages) {
			try {
				const event = message.body;

				if (shouldRemove(event)) {
					await removeRecipient(env, event);
				}

				message.ack();
			} catch (error) {
				console.error("Failed to process Email Sending event", {
					eventId: message.body.payload.eventId,
					error,
				});
				message.retry();
			}
		}
	},
} satisfies ExportedHandler<Env, EmailSendingEvent>;

function shouldRemove(event: EmailSendingEvent): boolean {
	if (event.type === "cf.email.sending.message.complained") {
		return true;
	}

	return (
		event.type === "cf.email.sending.message.bounced" &&
		event.payload.bounce?.type === "hard"
	);
}

async function removeRecipient(
	env: Env,
	event: EmailSendingEvent,
): Promise<void> {
	await env.RECIPIENTS.delete(event.payload.recipient);
	console.log("Removed recipient record", {
		eventId: event.payload.eventId,
		reason: event.type,
	});
}

存在しない KV キーの削除は成功します。そのため、同じイベントが繰り返し配信されても安全です。

ハンドラーは成功した各メッセージに対して ack() を呼びます。失敗した操作は、3 回再試行したあと デッドレターキュー に移ります。

Worker をデプロイする

Worker とキューコンシューマーの構成をデプロイします。

npx wrangler deploy

失敗したイベントはデッドレターキューを監視します。原因を直したあと、再処理します。

関連リソースを確認する

役に立ちましたか?