masalibの日記

システム開発、運用と猫の写真ブログです

Firebase入門 Cloud Functionsでメール送信(SendGrid)

エンジニア向けのサービスならSlackなどの通知で問題ないのですが、一般人向けだとやっぱりメールで通知する必要があります。(理想はLINE) サービスでは欠かせない問い合わせ機能を作るためにメール送信の機能を調べました。

最初はGoogleのメールで対応しようと思ったのですが、ローカルのエミュレータからのメール送信がGoogle認証(Oauth)の部分でうまくいかず諦めてしましました。

失敗したパターンは

  • NodeMailrでメール送信(from:gmail)
  • gmail-nodeでメール送信(from:gmail)

です。

問い合わせメールは月に1万も発生しないと思うので メール配信サービスの「SendGrid」を使う事にしました

この記事はReact Firebase入門シリーズです 1-1-1・ Firebase初期設定とFirebaseAuthのSignUp
1-1-2・ 「react-hook-form」を入れてみた
1-1-3・ AuthのSignUpの通信エラー対応
1-2 ・ FirebaseAuthのログイン処理
1-3 ・ FirebaseAuthのログイン認証とログアウト処理
1-4 ・ FirebaseAuthのパスワード初期化処理
1-5 ・ FirebaseAuthのメールアドレスの有効化
1-6 ・ FirebaseAuthのメールアドレスとパスワード変更
1-7 ・ FirebaseAuthの表示名変更
1-8 ・ FirebaseAuthの拡張項目追加
1-9-1 ・ FirebaseAuthのTwitter認証
1-9-2 ・ FirebaseAuthのTwitter認証(既存ユーザー向け)と解除
1-10 ・ FirebaseAuthのGoogle認証(既存ユーザー含む)と解除 2-1・ FirebaseStorageのファイルアップ:基礎
2-2・ FirebaseStorageのファイルアップ前に画像の切り抜き
2-3・ FirebaseStorageのファイルアップの移動
3-1・FirestoreのCRUD
3-2・Firestoreのデータ取得補足
3-3・Firestoreのページネーション処理
3-4・Firestoreのコレクション(テーブル)のJOIN
5・ FirebaseのHosting(デプロイ)
6-1・ Cloud FunctionsでHello,world
6-2・ Cloud Functions(エミュレータ)でHello,world
6-3・ ユーザーの作成時にCloud Functionsを実行
6-4・ Cloud Functionsでメール送信 今ここ
よかったら他の記事も見てください。

やりたい事

  • メール送信

事前作業

メール送信には「SendGrid」というサービスを使って配信します。配信にはAPIキーが必要なので取得したいと思います。

SendGridとは

メール送信APIです
SendGridのメール送信APIを使って、数分でシステム連携。 メール送信のノウハウが集約されたセキュアなインフラは 高い到達率や配信状況が見える

プラン 値段 通数上限値 上限超過時
無料 0円 12000通/月 -
40K 1,900円 40,000通/月 0.125円/月
100K 3,800円 100,000通/月 0.094円/月
100K_Pro 10,000円 100,000通/月 0.106円/月
300K_Pro 25,000円 300,000通/月 0.065円/月
700K_Pro 50,000円 700,000通/月 0.056円/月
1.5M_Pro 87,500円 1,500,000通/月 0.056円/月
1.5M_Pro 119,000円 2,500,000通/月 0.056円/月

Proの特典

  • 固定IPがもらえる
  • 請求書払いができる
  • サブユーザーの作成ができる(サービス単位でやれるみたいなので大きくなったら必要だと思う)

無料でも独自ドメインの設定ができる所がいいと思います。

const sgMail = require('@sendgrid/mail')
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
const msg = {
  to: 'test@example.com', // Change to your recipient
  from: 'test@example.com', // Change to your verified sender
  subject: 'Sending with SendGrid is Fun',
  text: 'and easy to do anywhere, even with Node.js',
  html: '<strong>and easy to do anywhere, even with Node.js</strong>',
}
sgMail
  .send(msg)
  .then(() => {
    console.log('Email sent')
  })
  .catch((error) => {
    console.error(error)
  })

なぜか公式のAPIのsourceはコピペできなかった。デベロッパーツールでclassを変更すればコピペできます

修正前

<div id="gistNNNNNN" class="gist">

修正後

<div id="gistNNNNNN" class="gxxxxist">

SendGridの登録

  1. サイトにアクセスする
  2. 新規登録のボタンを押す
  3. アカウント情報を入力
  4. 確認メールが届く
  5. 確認後にSMS認証する

すいません、画面キャプチャーをとっていませんでした

私だけかも知れないのですが、検索からいくログインページだとログインできず・・・
https://sendgrid.kke.co.jp/app?p=login.index

appの部分のログインだとうまくいくという不思議な状況・・・
https://app.sendgrid.com/login?redirect_to=%2F

APIキーの発行

  1. 管理画面にアクセスする
    https://app.sendgrid.com/
  2. メニューの「Settings」→「API Keys」とアクセスする
  3. Create API Key」のボタンを押す
  4. API Key Name」に適当な名前をいれる
  5. アクセス許可をFull Accessを選択する
  6. 作成後にキーが表示される(1回しか発行されないみたいなので必ずコピーする)

送信者認証の登録

必須というはわけではないのですが、 私みたいにFromのアドレスが「masalib@gmail.com」なのに「SendGrid」で送る場合など 送信ドメインと送信の表示アドレスが違う場合には登録した方がいいです。 (ドメインDNS設定にアクセスできない場合)
ドメイン認証と違って1つのアドレス単位で登録しないといけません。

  1. 管理画面にアクセスする
    https://app.sendgrid.com/
  2. メニューの「Settings」→「Sender Authentication」とアクセスする
    f:id:masalib:20201214172416p:plain
  3. Verify a Single Sender」のボタンを押す
  4. Create New Sender」のボタンを押す
  5. フォームの内容を入力する
  6. 認証がうまくいくとリストで認証になります

できていないけど本番では必要な事

サービスとして使う場合はサービスのドメインがあると思います。 そのドメインを「SendGrid」に登録した方がいいです。必須ではないのですが、迷惑メールフォルダにいかないようにするためにできる事はやった方がいいです。

プログラム

モジュールのインストール

SendGridが提供してくれているモジュールをインストールします

$ cd funcions
$ npm install @sendgrid/mail

環境変数の設定

githubで公開している関係でAPIキーをsourceに直接記載するわけにはいかないので登録します

$ cd Project_homeFolder
$ firebase functions:config:set sendgrid_service.key="XXXXXXXXXXXXAPIkeyXXXXXXXXXXXX"
$ firebase functions:config:set sendgrid_service.email="masalib@gmail.com"
$ firebase functions:config:set sendgrid_service.name="learn-firebase-masalib"
$ firebase deploy --only functions

エミュレータ環境変数を設定する

$ cd ProjectのhomeFolder
$ cd functions
$ firebase functions:config:get > .runtimeconfig.json

以下のようなファイルができていると思います

{
  "sendgrid_service": {
    "email": "masalib@gmail.com",
    "key": "XXXXXXXXXXXXAPIkeyXXXXXXXXXXXX",
    "name": "learn-firebase-masalib"
  }
}

runtimeconfig.jsongithubにアップしないようにするために 無視リストに追加します

# firebase emulator .env
.runtimeconfig.json

プログラムの修正

前回と同じで/funcions/index.jsを修正します。 プログラムは至ってシンプルです

const sendgrid  = require('@sendgrid/mail')
exports.sendMail = functions.https.onRequest(async (request, response) => {
    functions.logger.info("sendMail start");

    const apiKey = functions.config().sendgrid_service.key;
    const fromEmail = functions.config().sendgrid_service.email;
    const fromName = functions.config().sendgrid_service.name;

    sendgrid.setApiKey(apiKey);
    const msg = {
        to: "m-hirano@mediaseek.co.jp",
        from: `${fromName} <${fromEmail}>`,
        subject: `【${fromName}】問い合わせ受理メール`,
        text: `XXXX様
問い合わせありがとうございます。担当者が確認して返信したいと思いますので
少々お待ち下さい。

このメールには返信できません。`
    };

    sendgrid.send(msg).then((result )=>{
        functions.logger.info("sendMail success");
        response.send("sendMail success");
        return console.log("Successfully sent message:", result);
    })
    .catch((error)=>{
        functions.logger.error("sendMail error");
        functions.logger.error(error);
        response.send("sendMail error");
        return console.log("Error sending message:", error);
    })
    //return;
})

公式のサンプルとほとんど同じです。returnの部分を設定しないとeslintに引っかかってデプロイできませんwww

結果

メール送信に成功しました。 もちろん迷惑メールフォルダに行ってません。

感想

ローカルでメール送信するのにマジで時間がかかった。サーバーにアップするならもっと楽にできたんだけど本番にできてローカルでできない事はできれば減らしたかった

参考URL

tech.playground.style

qiita.com

qiita.com