Menu

メール通知

NEXTY.DEV には、ResendCloudflare Email Sending の2つのサービスプロバイダーに対応したメール送信抽象層が組み込まれており、よく使用されるユーティリティ関数とメールテンプレートがすでにラップされています。ゼロから設定する必要はなく、本ガイドの手順に従うだけで次のことが可能です。

  • 独自のメールテンプレートを追加(例:注文通知、会員期限切れリマインダーなど)
  • サーバーサイド Action でメールを送信
  • 送信者情報や配信停止リンクがベストプラクティスに準拠していることを保証

前提条件

メール通知機能を使用する前に、いずれかのメールサービスプロバイダーの設定を完了してください。

設定後、DEFAULT_EMAIL_PROVIDER 環境変数でデフォルトのプロバイダーを切り替えられ、ビジネスコードを変更する必要はありません。

一、全体アーキテクチャの概要

NEXTY.DEV において、メール関連のコアファイルは以下の通りです。

  • 統一メールエントリーポイント: lib/mail/index.ts
  • メールサービスプロバイダーの実装: lib/mail/providers/resend.tslib/mail/providers/cloudflare.ts
  • React メールテンプレートのレンダリング: lib/mail/render.tsx
  • 連絡先管理: lib/mail/contacts.ts
  • 具体的なビジネスシナリオ Action の例: actions/newsletter/index.tsactions/stripe/index.ts
  • React メールテンプレート: emails/*.tsx

すべてのメール送信呼び出しは、lib/mailsendEmail を経由することを推奨します。ビジネスロジック内で直接 new Resend() を使用したり Cloudflare SDK をインスタンス化したりしないでください。これにより以下が可能になります。

  • API Key、送信者メールアドレス、配信停止ロジックの統一管理
  • React テンプレートのレンダリングとプレーンテキスト fallback の自動処理
  • Resend と Cloudflare の間でシームレスに切り替え
  • 受信者を自動的に連絡先リストに追加(Resend は Contacts に同期、Cloudflare は Redis に記録)

二、環境変数と基本設定

コードを書き始める前に、以下の環境変数が設定されていることを確認してください。

環境変数説明
DEFAULT_EMAIL_PROVIDERデフォルトのメールサービスプロバイダー:resend または cloudflare、デフォルトは resend
DEFAULT_ADMIN_NAME送信者名(例:NEXTY.DEV Team
DEFAULT_ADMIN_EMAIL送信者メールアドレス(例:[email protected]
NEXT_PUBLIC_SITE_URLサイトの完全なドメイン名、メール内の絶対リンク生成に使用

Resend を使用する場合はさらに以下が必要です。

  • RESEND_API_KEY: あなたの Resend API Key

Cloudflare Email Sending を使用する場合はさらに以下が必要です。

  • CLOUDFLARE_EMAIL_API_TOKEN: Cloudflare API Token
  • CLOUDFLARE_ACCOUNT_ID: Cloudflare アカウント ID

ローカル開発環境では、.env.local で以下のように設定できます(Cloudflare の例)。

DEFAULT_EMAIL_PROVIDER=cloudflare
DEFAULT_ADMIN_NAME="NEXTY.DEV Team"
DEFAULT_ADMIN_EMAIL="[email protected]"
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
 
CLOUDFLARE_EMAIL_API_TOKEN=your_cf_email_api_token
CLOUDFLARE_ACCOUNT_ID=your_cf_account_id

三、コア送信ツール:sendEmail

lib/mail は統一された sendEmail メソッドを提供します。パラメータ定義は以下の通りです(簡略化した説明)。

interface SendEmailOptions {
  to: string | string[];                         // 受信者メールアドレス
  from: string | { email: string; name?: string }; // 送信者
  subject: string;                               // メール件名
  html?: string;                                 // HTML 内容(react と二択)
  text?: string;                                 // プレーンテキスト内容(オプション)
  replyTo?: string | { email: string; name?: string }; // 返信先アドレス
  cc?: string | string[];                        // CC
  bcc?: string | string[];                       // BCC
  headers?: Record<string, string>;              // カスタムメールヘッダー
  attachments?: EmailAttachment[];               // 添付ファイル
  react?: React.ComponentType<any> | React.ReactElement; // React メールテンプレート
  reactProps?: Record<string, any>;              // テンプレートに渡す props
  addToContacts?: boolean;                       // 送信成功後に受信者を連絡先リストに追加するかどうか
}

内部で以下のことを自動的に行います。

  • to が空の場合、エラーを返す
  • メール関連の環境変数が正しく設定されていない場合、エラーを返す
  • React テンプレートを使用する場合、HTML に自動レンダリングし、プレーンテキスト fallback を生成
  • DEFAULT_ADMIN_NAME + DEFAULT_ADMIN_EMAIL を組み合わせて送信者を構成(呼び出し時に上書きも可能)
  • DEFAULT_EMAIL_PROVIDER に基づいて対応するプロバイダーにルーティング
  • 送信失敗時に HTTP ステータスコードに基づいて1回自動再試行

プロバイダーの明示的指定

sendEmail の第2引数でプロバイダーを明示的に指定できます。A/B テストや異なるチャネルを同時に使用したいシナリオでよく使用されます。

import { sendEmail } from "@/lib/mail";
 
// デフォルトプロバイダーを使用
await sendEmail({ ...options });
 
// 明示的に Resend を使用
await sendEmail({ ...options }, "resend");
 
// 明示的に Cloudflare を使用
await sendEmail({ ...options }, "cloudflare");

四、組み込み例:Newsletter 購読のウェルカムメール

actions/newsletter/index.ts は完全な例を示しており、フローは概ね以下の通りです。

  • ユーザー入力のメールアドレスを正規化して検証
  • レート制限を実施(悪意のあるリクエストの防止)
  • メール件名と配信停止リンクを準備
  • sendEmail を呼び出して NewsletterWelcomeEmail テンプレートを送信

ロジックの断片:

await sendEmail({
  to: normalizedEmail,
  from: {
    email: process.env.DEFAULT_ADMIN_EMAIL!,
    name: process.env.DEFAULT_ADMIN_NAME || siteConfig.name,
  },
  subject: `Welcome to ${siteConfig.name} Newsletter!`,
  react: NewsletterWelcomeEmail,
  reactProps: {
    email: normalizedEmail,
    unsubscribeLink,
  },
  headers: {
    "List-Unsubscribe": `<${unsubscribeLink}>`,
    "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
  },
  addToContacts: true,
});

独自のビジネスではこの書き方を完全に参考にでき、以下を置き換えるだけです。

  • テンプレートコンポーネントreact フィールド)
  • テンプレート propsreactProps フィールド)
  • subject の文言

五、連絡先管理

lib/mail/contacts.ts は統一された連絡先管理インターフェースを提供します。

  • addContact(email, provider):送信成功後に呼び出されます。Resend は Resend Contacts に同期し、Cloudflare は Redis セットに記録します。
  • removeContact(email):配信停止時に呼び出されます。Resend Contacts と Redis レコードの両方を同時にクリーンアップするため、プロバイダーを切り替えた後も配信停止が有効なままです。
  • isContact(email):メールアドレスが Redis の連絡先セットに含まれているかを判定します。

ビジネスコードでは通常、これらのメソッドを直接呼び出す必要はありません。sendEmailaddToContacts: true が連絡先追加を自動的に処理します。

六、注意事項

  • 新しいメールテンプレートを作成する際は、異なるメールテンプレート間でスタイルの一貫性を保つ必要があります。これはブランドイメージにとって最も有益です。
  • メールテンプレート内のブランド情報は siteConfig.namesiteConfig.url を使用しているため、config/site.ts の設定が正しいことを確認する必要があります。
  • メールテンプレートでは lucide-react を使用できません。SVG アイコンまたは画像の絶対リンクを使用してください。
  • メールテンプレートでは Tailwind CSS を使用できません。インラインスタイルを使用してください。
  • メールテンプレート内では相対リンクを使用できません。絶対リンクを使用してください。
  • Resend の無料プランでは1000件の連絡先を保存でき、月間3000通まで送信可能です。これがニーズを満たさない場合、プランをアップグレードする必要があります。
  • Cloudflare Email Sending では送信者ドメインが DNS 検証を通過している必要があります。現在の main ブランチは REST API 経由で送信し、サーバーまたは Vercel デプロイに適しています。Cloudflare Workers デプロイを使用する場合は、cf-pg ブランチ を使用してください。

新しいメールテンプレートを作成する際は、AI に既存のメールテンプレートを参照させて新しいテンプレートを作成させるだけで、各詳細を手動でチェックする必要はありません。