API firstのメール基盤:Resendがメール認証に選ばれるシンプルな理由

はじめに
メール認証の実装でこんなことありませんか?
自前認証を実装することになり、登録確認メールとパスワードリセットメールの送信機能も自分で作らなければならなくなった。
SendGridやSESを調べたが、APIキーの設定・IPウォームアップ・バウンス管理など、メール送信を運用する複雑さに圧倒された。
Edge Runtime上から呼び出せるシンプルなメール送信APIがほしいが、適切なサービスの選び方がわからなかった。
この記事をお勧めしない人
- すでにSendGridやSESを使っており、メール基盤の移行を検討していない人。
- バルクメール(マーケティングメール)の大量配信が主目的で、トランザクショナルメールが不要な人。
- メールテンプレートをHTMLファイルで管理することに問題がなく、React Emailへの移行に関心がない人。
もし一つでも当てはまらないなら、読み進める価値があるかもしれません。
メール送信の複雑さが認証基盤全体の足を引っ張る
- SMTPサーバーの設定やIPウォームアップの管理が必要になり、メール配信の設定に想定外の工数がかかる。
- バウンスや迷惑メール報告の処理をWebhookで受け取る実装が増え、認証フローとは無関係なコードが膨らむ。
- Edge Runtimeで動かないNode.js依存のメールライブラリを使い続けることで、認証基盤全体の環境依存が広がる。
APIを呼ぶだけで送れるという明るい未来
- この記事を読めば、HTTP POST一発でメールを送信できるResendを使い、メール基盤の運用コストをほぼゼロにできる明るい未来があります。
- 具体的には、React Emailでテンプレートを型安全に管理し、Edge Runtimeから直接呼び出すボイラープレートを手に入れられます。
- この方法は、筆者が運営するReact Router × Cloudflare Edgeのブログのメール認証基盤として実際に稼働しています。
- この情報は、「Cloudflare Workers + 自前認証 + Resend」という組み合わせの実践知として、巷の記事では決して得られない実装の勘所を含んでいます。
このブログも同じ選定をした
メール認証基盤を設計する過程で、SendGridの複雑さとnodemailerのEdge非対応という2つの壁に当たりました。そこで「HTTPを話せる環境から呼べる」という条件だけで選んだのがResendです。この記事では、その判断の根拠とClaudeMixでの実装パターンを持ち帰れるように書きました。D1・KVの記事と合わせて読むと、Cloudflareネイティブ層の全体像が見えてきます。なお、D1(ユーザー)・KV(セッション)・Resend(メール)の組み合わせは、実質的に「Build your own Clerk」の最小構成です。認証SaaSを使わずEdgeで自前Authを設計する記事は日本語圏でほぼ存在しない——この記事シリーズの希少性はそこにあります。
延命医の診断
メール送信の本質はHTTPである
SMTPは1970年代から続くプロトコルで、TCP接続・stateful・queue・retryを前提とした設計だ。つまり【メールサーバー運用前提】。SendGridやSESの複雑さ(IPウォームアップ・バウンス管理・SMTP relay)は設計思想の問題ではなく、SMTP世界をそのままAPI化した結果だ。
Cloudflare Workers(Edge Runtime)はTCP socketを直接開けない。HTTPフェッチしか使えない。だから【Edge時代のメールAPIはHTTPしかありえない】。Resendはこの構造を突いた——SMTPを裏側に押し込め、開発者にはHTTPだけを見せる。
Workers → HTTP → Resend → SMTP(裏側)「HTTPを話せる環境ならどこからでも送れる」。これがEdgeとResendの整合性が構造的に保証される理由だ。
SendGrid → SES → Resendという淘汰の文脈
SendGridはメールインフラを提供した第1世代。SESはAWSのエコシステムにメールを組み込んだ第2世代。Resendは「開発者がAPIとして使いたい」という需要だけを切り出した第3世代だ。2023年創業ながら急速に開発者コミュニティに広がったのは、過剰な機能を削ぎ落として1つのことだけを正しくやっているから。React Emailとの公式統合もこの思想の延長にある。
React Emailとの接合点
HTMLメールはWebのCSSの一部が動かず、テンプレートの管理が煩雑になりやすい。React Emailはこの問題をJSXで解決する。ResendとReact Emailは同じ開発者(Zeno Rocha)が作っており、テンプレート生成と配信が【一体設計】になっている。普通はテンプレートエンジンと送信サービスは別会社だ。この2つは設計思想が同一であるため、react: <Component />という1行でtemplate → deliveryが完結する。型安全なテンプレート管理とHTTP API送信の組み合わせは、AIにコンテキストを渡す際も単純な関数呼び出しとして表現できる。
選定理由
独立選択の根拠
ResendがCloudflareネイティブ層の一角を担う積極的な理由は、選定軸の4条件を個別に満たしているからだ。
Edge Runtimeとの整合は構造的に保証されている。ResendのSDKは内部でFetch APIを使用しており、Node.js固有のAPIに依存していない。「Edge Runtimeで動くか?」という確認作業が不要で、セットアップ後すぐに動く。
外部SaaS依存の最小化にも寄与する。Resendは単なるHTTPエンドポイントであり、インフラに組み込まれない。Stripeのようにサービスの存続に直結する依存ではなく、同等のHTTP APIを持つサービスへの切り替えコストが低い。
APIキーの発行からメール送信まで数分で完結する設計になっており、IPウォームアップやSPF/DKIMの初期設定も管理画面から直感的に行えます。
なぜ代替案を選ばなかったか
| 技術 | 不採用理由 |
|---|---|
| Clerk | 認証SaaSとして認証メールはClerk内部で処理するためResendは不要になる。ただしユーザーDB・セッション・認証フローがすべてClerk側に置かれ、データ主権が外部に移る。自前Authとは思想が異なる。 |
| SendGrid | 機能が豊富すぎてトランザクショナルメール用途には過剰。バウンス管理・Webhook・IPウォームアップなど運用コストが高い。 |
| nodemailer | Node.js依存のため、Cloudflare Workers(Edge Runtime)では動作しない。ライブラリ依存が実行環境制約と矛盾する。 |
| Amazon SES | AWSアカウントとIAMポリシーの管理コストが増加する。Cloudflareと異なるベンダーを追加することで外部依存が分散する。 |
| Mailgun | APIは類似するが、React Emailとの公式統合がない。テンプレート管理を別途設計する必要が生じる。 |
構造的メリット
Edge Runtime完全対応のHTTP APIによる呼び出し:ResendのSDKはFetch APIを内部で使用しており、Node.js固有のAPIに依存していません。Cloudflare Workersから直接インポートして使えるため、Edge Runtime対応の確認作業が不要です。
React Emailによる型安全なテンプレート管理:メールのHTMLをJSXで記述できるため、テンプレートのリファクタリング時にTypeScriptの型チェックが機能します。HTMLメールの文字列管理で起きやすい「タグの閉じ忘れ」「変数の埋め込みミス」がコンパイル時に検出されます。
シンプルな料金体系と運用コスト:Resendは1日100通・月3000通まで無料です。小規模SaaSの認証メール用途なら数千ユーザー規模まで無料枠で十分に運用できます。バウンス管理やSPF/DKIMの設定もダッシュボードで完結するため、インフラ管理の負担が最小化されます。
ClaudeMixでの活用例
以下はResendとReact EmailをCloudflare Workersから呼び出すボイラープレートです。
// app/data-io/account/email/sendVerificationEmail.server.ts
import { Resend } from "resend";
import { VerificationEmailTemplate } from "~/components/email/VerificationEmail";
// Resendクライアントの初期化(APIキーはCloudflare Secretで管理)
function getResendClient(apiKey: string): Resend {
return new Resend(apiKey);
}
export type SendVerificationEmailResult =
| { success: true }
| { success: false; error: string };
// メール認証用のトークンをメール送信する
export async function sendVerificationEmail(
apiKey: string,
to: string,
verificationToken: string
): Promise<SendVerificationEmailResult> {
const resend = getResendClient(apiKey);
const verificationUrl = `https://example.com/account/verify?token=${verificationToken}`;
const { error } = await resend.emails.send({
from: "noreply@example.com",
to,
subject: "メールアドレスの確認",
react: VerificationEmailTemplate({ verificationUrl }),
});
if (error) {
return { success: false, error: error.message };
}
return { success: true };
}// app/components/email/VerificationEmail.tsx(React Email テンプレート)
import {
Html,
Head,
Body,
Container,
Text,
Link,
Preview,
} from "@react-email/components";
type Props = {
verificationUrl: string;
};
// React EmailでHTMLメールをJSXとして型安全に定義する
export function VerificationEmailTemplate({ verificationUrl }: Props) {
return (
<Html>
<Head />
<Preview>メールアドレスの確認</Preview>
<Body style={{ fontFamily: "sans-serif" }}>
<Container>
<Text>以下のリンクをクリックして、メールアドレスを確認してください。</Text>
<Link href={verificationUrl}>メールアドレスを確認する</Link>
<Text>このリンクは24時間有効です。</Text>
</Container>
</Body>
</Html>
);
}// app/routes/account/register.tsx(React Router ActionでのResend呼び出し例)
import type { ActionFunctionArgs } from "@remix-run/cloudflare";
import type { Env } from "~/types/cloudflare";
import { sendVerificationEmail } from "~/data-io/account/email/sendVerificationEmail.server";
import { generateToken } from "~/lib/account/auth/token.server";
export async function action({ request, context }: ActionFunctionArgs) {
const env = context.cloudflare.env as Env;
const formData = await request.formData();
const email = String(formData.get("email"));
// 認証トークンを生成してD1に保存する(別途実装)
const token = await generateToken(env.DB, email);
// ResendでメールをCloudflare Secret経由で送信する
const result = await sendVerificationEmail(env.RESEND_API_KEY, email, token);
if (!result.success) {
return { error: "メール送信に失敗しました。しばらくお待ちください。" };
}
return { message: "確認メールを送信しました。メールをご確認ください。" };
}エラーハンドリングの思想として、メール送信の失敗はユーザーに対して「しばらくお待ちください」と汎用的に返します。Resendのエラーレスポンスには詳細なエラーコードが含まれますが、ユーザーに送信先の問題(メールアドレス不正等)を詳しく伝えすぎることは、不正なアカウント調査(User Enumeration)につながるリスクがあります。ログには詳細を残しつつ、ユーザーへの表示は最小化する設計が適切です。
公式リファレンス・更新履歴
- Resend 公式ドキュメント
- Resend Node.js SDK リファレンス
- React Email 公式ドキュメント
- Cloudflare Workers での Resend 使用例
- 最終確認日: 2026-03-13
- 次回確認予定: Resend SDKのメジャーバージョンアップ時
メール基盤が整い、ユーザー登録・セッション管理・メール送信がCloudflare上で完結した。次に向き合うのは収益化だ。決済基盤はどこに置くか——外部SaaSへの依存をどう設計するかという問いが、Stripeの選定につながる。
