【第1回】完全無料で構築するモダンメールサーバー!Cloudflare Workers × TypeScript × Resendで自動応答システムを作ろう

typescript-resend プログラミング

今回は、サーバーの維持費を永年0円(無料枠内)に抑えつつ、信頼性の高い独自ドメインのメール送受信環境を構築する方法を解説します。

全数回にわけて解説します。第1回目は、システム全体の概要と、プログラムから独自ドメインでテストメールを送信するまでの手順を進めていきます。

環境はWindows11を想定しています。Macの方はターミナルコマンドを適宜変更してください。

1. 今回構築するメールシステムの概要

今回構築するのは、「Cloudflare Workers」「TypeScript」、そして次世代のメール配信サービス「Resend」を組み合わせた超軽量・高速なサーバーレスメールシステムです。

構成の全体像とメリット

  • Cloudflare Workers: サーバーを持たずにプログラムを実行できる環境(サーバーレス)です。メールの送受信イベントが発生した瞬間だけ起動するため、無駄な待機コストが一切かかりません。
  • TypeScript: 型安全で堅牢なコードを記述し、バグの少ないクリーンな開発を進めます。
  • Resend: 非常に開発者フレンドリーなメール配信APIです。無料枠が非常に寛大で、到達率(迷惑メールに入りにくさ)が極めて高いのが特徴です。

これらを組み合わせることで、従来のようなVPSのメールサーバー構築の手間や、ブラックリスト入りのリスクから解放された快適なメール環境が手に入ります。

2. Resendの初期設定とドメイン認証

まずはメール送信の要となる Resend のセットアップを行います。

独自ドメインの登録とDNS認証

  • Resendのダッシュボードにサインアップし、ログインします。
  • 左メニューの 「Domains」「Add Domain」 をクリックします。
  • お持ちの独自ドメイン(例: go-pro-world.net)を入力し、Regionに ap-northeast-1(東京)などを指定して登録します。
  • 画面に表示される DNSレコード(MXレコード、TXTレコード等) を、ドメインのDNS管理画面(Cloudflareなど)に設定するか、確認ボタンがあればクリックだけで設定が書き換えられます。
  • 以下の画面のように Status が Verified になればOKです。

💡 ポイント: これによりSPFやDKIMといったメールの送信ドメイン認証が完了し、Gmail等に送っても「なりすまし」と判定されず、確実に受信トレイに届くようになります。

API Keyの取得

認証が完了したら、プログラムからResendを操作するための鍵を取得します。

  • 左メニューの 「API Keys」「Create API Key」 をクリック。
  • Nameに Workers-Mail などと入力し、Permissionは Full Access を選択して作成します。
  • 発行された re_xxxxxxxxxxxx というAPIキーを安全な場所にコピーしておきます。

3. プロジェクトの作成とコードの実装

ここからはローカル環境(PC)で開発を進めます。

npmでプロジェクトを作成する

ターミナル(PowerShellやMacのターミナル)を開き、プロジェクト作成のコマンドを入力します。

 npm create cloudflare@latest mail-serverless

​🔍 コマンドの仕組みを分解

  • ​npm (Node Package Manager)
    • ​Node.jsというJavaScript/TypeScriptの環境で使えるライブラリ管理ツールです。
  • ​create
    • ​npmが持つ特別な機能で、「新しくプロジェクト(ひな型)を作るとき専用」の命令です。内部的には npm exec create-cloudflare という仕組みを裏側で走らせています。
  • ​cloudflare@latest
    • ​「Cloudflare公式が提供しているプロジェクト作成ツール(create-cloudflare)の最新版(latest)を使ってください」という意味です。
  • ​mail-serverless
    • ​あなたがこれから作るプロジェクト名(フォルダ名)です。自由に決められます。

いくつか質問されるので以下のように選択していきます。

 What would you like to start with? 
  ● Hello World example 

  Select from barebones examples to get started with Workers  
╰ Which template would you like to use? 
  ● Worker only 

 Which language do you want to use? 
  ● TypeScript

Do you want to add an AGENTS.md file to help AI coding tools understand Cloudflare APIs? 
 ●  No

次に、Resendの公式SDKをインストールします。

npm install resend

設定ファイル(wrangler.jsonc)の修正

プロジェクトのルートにある wrangler.jsonc を開き、以下のように本番環境用の基本設定を記述します。

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "mail-serverless",
  "main": "src/index.ts",
  "compatibility_date": "2024-04-03",
  "observability": {
    "enabled": true
  },
  "vars": {
    "RESEND_API_KEY": "re_xxxxxxxxxxxxxxx" // ローカルテスト時に一時的にキーを入れる場所
  }
}

送信プログラム(src/index.ts)の実装

src/index.ts の中身を以下のように書き換えます。Webからのリクエスト(POST)を受け取って、Resend経由でメールを送信するシンプルなAPIを作成します。

import { Resend } from 'resend';

// Cloudflare Workersの環境変数を定義する型(インターフェース)
// wrangler.jsonc や Cloudflareのダッシュボードで設定した変数を受け取ります
export interface Env {
  RESEND_API_KEY: string; // Resendの管理画面から取得したAPIキーが入る
}

export default {
  // ■ Webからのリクエスト(HTTPリクエスト)が発生したときに自動的に実行されるメイン関数
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    
    // リクエストされたURLの情報を解析する(パス名などを取得するため)
    const url = new URL(request.url);
    
    // 【セキュリティ&ルーティング制限】
    // メソッドが「POST」かつ、アクセス先が「/send」以外のリクエストはすべて404エラー(Not Found)で弾く
    if (request.method !== 'POST' || url.pathname !== '/send') {
      return new Response('Not Found', { status: 404 });
    }

    try {
      // 送信されてきたJSONデータ(リクエストボディ)を解析し、必要な値を取り出す
      // to: 送信先アドレス, subject: 件名, text: 本文
      const { to, subject, text } = await request.json() as { to: string; subject: string; text: string };

      // 環境変数にResendのAPIキーが正しく設定されているかチェック
      if (!env.RESEND_API_KEY) {
        return new Response('API Key is missing', { status: 500 });
      }

      // Resendの送信クライアントを初期化(APIキーを渡す)
      const resend = new Resend(env.RESEND_API_KEY);
      
      // ResendのAPIを使って、実際にメールを送信する
      const { data, error } = await resend.emails.send({
        from: 'info@go-pro-world.net', // 認証を完了させたあなただけの独自ドメインアドレス
        to: [to],                      // 送信先(配列で指定)
        subject: subject,              // メールの件名
        text: text,                    // メールの本文(テキスト形式)
      });

      // Resend側で送信エラー(ドメイン未認証やアドレス不備など)が発生した場合の処理
      if (error) {
        return new Response(JSON.stringify({ error }), { status: 400 });
      }

      // 送信がすべて成功した場合、結果のデータと一緒にステータス200(成功)を返す
      return new Response(JSON.stringify({ message: 'Email sent successfully', data }), { status: 200 });
      
    } catch (err: any) {
      // JSONのパース失敗など、予期せぬプログラムエラーをキャッチして500エラーを返す
      return new Response(JSON.stringify({ error: err.message }), { status: 500 });
    }
  }
};

4. 開発サーバーの起動と送信テスト

開発サーバーの立ち上げ

コードが書けたら、ローカル環境でWorkersを起動します。

npm run start

ターミナルに http://localhost:8787 と表示されれば、ローカル開発サーバーが正常に起動しています。

コマンドでテストメールを送信する

サーバーを起動したまま、別のターミナルウィンドウを開き(またはcurlが実行できる環境で)、以下のコマンドを実行してメール送信テストを行います。

PowerShellでのテストコマンド例

Invoke-RestMethod -Uri "http://localhost:8787/send" -Method Post -ContentType "application/json" -Body '{"to":"あなたのテスト用メールアドレス@gmail.com", "subject":"テストメール", "text":"サーバーレスメールシステムからの初送信です!"}'

​🔍 コマンドの仕組みを分解​

  • nvoke-RestMethod​
    • PowerShellで「外部のWebサイトやAPIとデータのやり取りをする」ための専用の機能(コマンドレット)です。
  • ​-Uri “http://localhost:8787/send”
    • データの送信先(宛先URL)です。localhost:8787 はあなたのパソコン内で立ち上がっているCloudflare Workersの開発サーバーを指しており、コード内で指定した /send というエンドポイントを呼び出しています。
  • ​-Method Post​
    • データを「新規登録・送信」するときに使われるHTTP通信の規格(POSTメソッド)を指定しています。​
  • -ContentType “application/json”
    • ​プログラムに対して、「今から送るデータは JSON(ジェイソン)形式 というプログラムが読みやすい形式のテキストですよ」と教えてあげるための設定です。​
  • -Body ‘{“to”:…, “subject”:…, “text”:…}’​
    • 実際に送信する**メールの中身(データ本体)**です。先ほど修正した src/index.ts のプログラムが、この to、subject、text という名前を読み取って、そのままResendに引き渡してメールを送信します。

コマンドを実行し、少し待って指定したテスト用アドレスにメールが届いていれば、第1段階の送信テストは見事成功です!

次回予告

第1回目は、ResendとCloudflare Workersを連携させ、プログラムからメールを送信する基盤を作りました。

次回は、いよいよ「メールの受信と自動応答」の実装に入ります。CloudflareのEmail Routingを活用し、送られてきたメールをWorkerでキャッチして日本語で自動返信する仕組みを構築します。

コメント

タイトルとURLをコピーしました