1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nuxt + Firebase + SendgridのDynamic Templateを使ってメール配信を実装する

Posted at

この記事を書いた背景

Firebaseで作成しているWEBアプリにメール配信機能を実装する段階になった。
外部のメール配信サービスで何を使うか比較検討して、SendgridのDynamic Templateを採用することにした。
理由としては、、、

  1. 無料配信枠(12,000通 / 月)で当分は足りる
  2. 非エンジニアの人が、GUIから文面を変更できるようにしたかった
  3. メールの既読管理(開封率、リンククリック etc)をしたかった
  4. Sendgrid APIの利用経験がある

SendgridのDynamic Templateに関しては、下記の記事が分かりやすい。
https://sendgrid.kke.co.jp/docs/Tutorials/A_Transaction_Mail/using_dynamic_templates.html
https://dev.classmethod.jp/articles/sendgrid-dynamic-templates/

実現させたい事

  1. 非エンジニアの人が、GUIから文面を変更できるようにしたかった
  2. メールの既読管理(開封率、リンククリック etc)をしたかった
  3. メールの送信タイプが20種類あるので、効率よくcodeを書きたい
  4. メールの送信自体は、非同期で構わないのでfunctions側で実行したい

悩んだ点

メールの配信プログラムの設計について、かなり時間をかけた。
というのも、メールの配信タイミングとしては、大きく3つが存在する

  1. フロント(nuxt)のユーザーアクションがトリガー
  2. functions側のonCreateなどのトリガー
  3. 毎朝9時に実行などの、Cron的なトリガー

3つを包括する方法として、functions側のmoduleにて、sendGridへの送信を記述。
各トリガーからは、payloadとして、各種IDとtemplateID(Dynamic Template)を送るだけにした。

例として、ユーザーが他の人の写真に、コメントをした場合

1. ユーザーがコメントしたタイミングで、nuxtからonCallで、functions(e.g userCreate ComentOnCall.js)を呼び出す
2. userCreateComentOnCall.js では、functions/module/sendEmail.jsをimportする
3. functions/module/sendEmail.js にて、SendGridのAPIを利用してメールを送信する

2のファイル

functions/src/sendEmailOnCall.js
const functions = require("firebase-functions");
const sendEmail = require("./module/sendEmail");

module.exports = functions.region("asia-northeast1").https.onCall(
  sentryWrapper(async (data) => {

    await sendEmail(data);

    return { status: "OK" };
  })
);

3のファイル

functions/src/module/sendEmail.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");

const sgMail = require("@sendgrid/mail");
const API_KEY = functions.config().sendgrid.key;
const FROM_EMAIL = "SampleApptest@gmail.com";


module.exports = async function(obj) {

  const userInfo = await admin
    .firestore()
    .doc(`userInfos/${obj.userId}`)
    .get()
    .then((doc) => {
      return doc.data();
    });

  // pattern毎に代入
  let templateId = "";
  let subject = "";

  // 開発モードは、タイトルに環境名を追記
  if (functions.config().functions.env !== "production") {
    subject += `<${functions.config().functions.env}>`;
  }

  switch (obj.type) {
    case 1: {
      subject += "ユーザー登録完了";
      templateId = "d-0db91b92c70142cfXXXXXXXXXXXXX";
      break;
    }
    case 2: {
      subject += `変更申請 - SampleApp - `;
      templateId = "d-82e84fd314fa410fXXXXXXXXXXXXX";
      break;
    }
    case 3: {
      subject += `変更承認 - SampleApp - `;
      templateId = "d-f17e889213464af8XXXXXXXXXXXXX";
      break;
    }
    case 4: {
      subject += `新着メッセージ - SampleApp -`;
      templateId = "d-6ccd61a728c548XXXXXXXXXXXXX";
      break;
    }
  }

  const links = {
    login: functions.config().basic.base_url + "login",
    qa: functions.config().basic.base_url + "helps/qa",
    cancel: functions.config().basic.base_url + "helps/cancel-policy",
    mypage:
      functions.config().basic.base_url +
      `users/${parentInfo.id}`,
  };

  let address = await admin
      .auth()
      .getUser(userInfo.id)
      .then((userRecord) => {
        return userRecord.email;
      });

  const payload = {
    email: {
      templateId: templateId,
      subject: subject,
      address: address,
    },
    user: {
      id: userInfo.id,
      name: `${userInfo.name.last} ${userInfo.name.first}`,
    },
    links: links,
  };

  const msg = {
    from: FROM_EMAIL,
    to: address,
    templateId: templateId,
    dynamic_template_data: payload,
  };

  sgMail.setApiKey(API_KEY);

  try {
    await sgMail.send(msg);
    return { status: "OK" };
  } catch (err) {
    return { status: "NG", text: err };
  }
};

上記ファイル内の、template-idである、d-xxxxxxxxxx というのが、Sendgridで作成したDynamic TemaplateのIDになる。
また、SendgridのAPI keyは、事前にfunctions.config().set より、登録しておく必要がある。

今回は、セキュリティーを考慮して、emailのアドレスはAuthenticationから取得。firestoreに保存しても問題はないと思うけど、念のために。

SendgridのDynamic Templateの書き方に関して

Dynamic Templateでは、メールテンプレートの作成方法として「Design Editor」と「Code Editor」の2種類が存在する。
どちらも、mustache記法という書き方で、動的に変数を表示できる。
mustache記法の書き方に関しては、下記を参考に。
https://qiita.com/sengok/items/1d958348215647a5eaf0

今後の課題

今回、メールの送信機能をfunctions側で共通化したことで、Code自体は綺麗に収まったのだが、構築段階での確認作業は大変。というのも、OnCreateや、Authenticationと連携したメソッドを試すには、Localのemulatorでは無理なので、毎回、firebaseにdeployする必要がある。反映までに、数分かかる。。

functionsのデプロイ時間を短縮する方法があったら、知りたい。。。

最後に。。。

今回の非エンジニアの方は、メールの文面をGUIから変更したいというニーズは、どこの会社もあると思う。
そういった観点からは、SendgridのDynamic Templateは有効だと思う。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?