はじめに
バックエンドの藤岡です。
今回はSendGridという簡単にメールを送れるサービスを使ってみたので、基本的な使い方をまとめてみようと思います。SendGrid自体は様々な言語や使用形態があるそうですが、今回はCloud Functions for Firebase上でNode.jsとして使っていきます。
前提知識
- Cloud Functionsの基礎知識
- Firestoreの基礎知識
- TypeScriptの基礎知識
前提条件
- Firestore、Cloud Functionsが環境構築してある
- SendGridのアカウントを作成して、API Keyを作成してある
全体の流れ
今回は「送信完了メール」を想定して、ユーザーがFirestoreにドキュメントを追加したことを感知して、そのユーザーにメールを送信するところまでを作ります。
大学の課題を提出したら「課題が提出されました」のようなメールが届きますよね。今回紹介するのはそのようなイメージです。
なお、Firestoreへの保存までは本筋とズレるため、今回は省略させていただきます。
環境変数の設定
SendGridのAPIキーや、メールを送信するアドレスは第三者に見られてしまうと悪用される恐れがあるため、環境変数に格納しておく必要があります。
今回はサーバー上の処理をCloud Functions上で行うため、Cloud Functionsの環境構築に格納しておきます。
$ firebase functions:set sendgrid.apikey="xxx"
$ firebase functions:set sendgrid.sendingaddres="yyy@zzz"
セットした環境変数はデプロイすることでCloud Functionsのサーバー上に保存されます。
$ firebase deploy --only functions
環境変数に入れているものは get
をすることで取得できるため、念の為ちゃんと保存されているか確かめておきましょう。
$ firebase functions:get
無事環境変数に登録できたら、コード上で以下のように取得できます。
const apikey: string = firebase.config().sendgrid.apikey;
Cloud FunctionsでSendGridを初期化する
SendGridを使うには、初めにAPIキーを使ってSendGridを初期化します。
使用するAPIキーは先程環境変数に格納したものを使います。
import * as sgMail from "@sendgrid/mail";
sgMail.setApiKey(functions.config().sendgrid.apikey);
Firestore登録時にCloud Functionsを実行する
SendGrid側の準備は整ったので、次に特定のドキュメントの作成を監視する関数をCloud Functionsに作ります。今回は MailTest/{id}
ドキュメントの作成を監視します。
export const observeDoc = functions
.region("asia-northeast1")
.firestore.document("MailTest/{id}")
.onCreate(async (snapshot, context) => {
// ドキュメント作成時に以下が実行される
// ...
});
Firestoreのデータを基にメールを作成
ユーザーにより新規追加されたドキュメントのデータをもとに、送信するメール内容を作成します。
const sendingAddres = functions.config().sendgrid.sendingaddres;
const msg: sgMail.MailDataRequired = {
to: snapshot.data().authorMail,
from: sendingAddres,
subject: "sendgrid test",
text: "this is a test mail sent by sendgrid.",
};
今回は、ドキュメントのスナップショットを用いて送信先のメールアドレスを取得していますが、送信先が登録したユーザー自信なら admin.auth()
を用いたほうが楽ですね。
また、送信するメール内容にはHTMLも挿入可能です。以下は公式ドキュメントの例です。
const msg = {
to: 'recipient@example.org',
from: 'sender@example.org',
subject: 'Hello world',
text: 'Hello plain world!',
html: '<p>Hello HTML world!</p>',
};
メールの送信
最後に作成したメールを送信します。
送信関数の型は以下のようになっています。
MailService.send(data: sgMail.MailDataRequired | sgMail.MailDataRequired[], isMultiple?: boolean | undefined, cb?: ((err: Error | sgMail.ResponseError, result: [...]) => void) | undefined): Promise<...>
これだけではよくわからないですね。簡単に説明しますと、 send
関数には3つの引数を取ります。
第1引数は送信するメールを入れます。複数送信する場合はメールのリストを入れます。
任意第2引数には複数送信するかどうかのbooleanを入れます。つまり複数送信するときは sgMail.send(msg[], true)
とします。
第3引数はコールバック関数を入れます。正常に動いた場合には error
は null
となり、エラーとなった場合には result
が null
となります。
今回は送信が完了したらコンソールにメッセージを送り、エラーとなったらエラーを返すようにします。
await sgMail.send(msg, false, (err, result) => {
// エラーとなりメールが送れなかった場合
if (err)
throw new functions.https.HttpsError(
"cancelled",
"メールを送信できませんでした"
);
// メールを送れた場合
console.log("メールを送信しました");
});
これにて、メールの送信は完了です。
最後にコード全体を確認します。
import * as sgMail from "@sendgrid/mail";
// 引数にはFirebaseの環境データに保存したApikeyを使用
sgMail.setApiKey(functions.config().sendgrid.apikey);
export const observeDoc = functions
.region("asia-northeast1")
.firestore.document("MailTest/{id}")
.onCreate(async (snapshot, context) => {
// メール作成
const sendingAddres = functions.config().sendgrid.sendingaddres;
const msg: sgMail.MailDataRequired = {
to: snapshot.data().authorMail,
from: sendingAddres,
subject: "sendgrid test",
text: "this is a test mail sent by sendgrid.",
};
// メール送信
await sgMail.send(msg, false, (err, result) => {
// エラーとなりメールが送れなかった場合
if (err)
throw new functions.https.HttpsError(
"cancelled",
"メールを送信できませんでした"
);
// メールを送れた場合
console.log("メールを送信しました");
});
});
おわりに
今回はCloud Functions上でSendGridを使う方法について紹介しました。今回紹介したのはSendGridの機能のほんの一部ですので、機会があればSendGridについてのまとめを書くかもしれません。最後に、本記事が少しでも参考になれば幸いです。
余談
実際にCloud Functionsで開発する場合、ほとんどの方はエミュレータを用いると思います。その場合、環境変数をデプロイする前にエミュレータ上で使ってみたいと思う方はいると思いますが、実はデプロイ前に環境変数をエラー上で取得することはできません。取得しようとすると以下のようなエラーが出ると思います。
Cannot read property 'apikey' of undefined`
そのため、一度セットした環境変数を別途JSONファイルに書き出しておく必要があります。書き出しには functions
ディレクトリで以下を実行すればOKです。
$ firebase functions:config:get > .runtimeconfig.json
こうすれば、 runtimeconfig.json
ファイル内に環境変数が格納されます。勿論、これは一時的な措置であるため、間違ってGithubで公開しないように気をつけましょう。 .gitignore
に登録するか、用済みになったらファイルを削除しておくと良いと思います。