SupabaseとResendでサクッとメール送信機能を作ってみよう


# メール送信機能の構築
現在Flutterでアプリケーションを作成しています。
データべースの更新に応じて、メールを送信する機能を構築してみたい、と思ったのでやってみました。
データベースはSupabaseを用いています。

https://supabase.com/

メールサーバーといった小難しい話になるのかと最初思いましたが、
Resendを使ってしまえば、すぐにできることがわかりました。
今回Resendは深く記載しませんが、
**この場ではメール送信機能の構築でも役立つサービス、とさせていただきます。**

https://resend.com/docs/introduction

## 読んでいただく前に
本記事について、以下のご経験があればスムーズに作業可能かと思います。
- クラウドサービスのサーバレス開発
- APIキーを用いることで動作するプログラムの作成

## Edge Functions経由でメール送信

https://supabase.com/docs/guides/functions/examples/send-emails

メール送信機能を作成する際、上記のリンクの内容を参考にしました。
参考にしました、というより、ほとんどそのままですかね。

Supabaseには`Edge Functions`というサービスがあります。
AWSのLambda Functionsを同じようにサーバーレスで処理を構築できます。
この処理で、ResendのAPIを呼び出してメール送信を実施します。

https://supabase.com/docs/guides/functions

:::note warn
警告
先に断っておきますが、今回はドメインの設定をしない状態で作業をしています。

ドメインを登録しない状態でもメール送信は可能ですが、
**Resendに登録したアドレス以外には送信できない**ので、注意いただきたいです。

```json
{
  "statusCode": 403,
  "message": "You can only send testing emails to your own email address (xxxxx@gmail.com). To send emails to other recipients, please verify a domain at resend.com/domains, and change the `from` address to an email using this domain.",
  "name": "validation_error"
}
```
Resendに登録したメールアドレス以外に対してメール送信しようとした場合、Edge Functionsのレスポンスが上記のようになります。
（`xxxxx@gmail.com`がResendに登録したメールアドレス）

:::

## メール送信だけならすぐ構築できてしまう
公式ドキュメント通りやれば、メール送信自体はすぐに（30分前後）終わってしまいます。作業は以下くらいです。
1. Resend APIの取得（アカウント作成後）
1. Edge Functionsを作成
1. 関数を実行

### Resend APIの取得
アカウント作成は完了した前提で進めます。
マネジメントコンソールから、の`API Keys`から`Create API Key`をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/ed1e2e3d-afeb-4c40-9afd-e750dfbe3ba2.png)

メール送信のみ実施できればOKなので、
Permissionは`Sending access`にしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/e965421a-e674-4900-bfe7-243ca08db15e.png)

これで作成が完了したので、値をコピーしておいてください。
Edge Functionsを作成する際に使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/f723a690-ed38-416b-aad8-934fc5d15521.png)

### Edge Functionsを作成
以下リンクのセクションに記載されたコードを使います。

https://supabase.com/docs/guides/functions/examples/send-emails#2-edit-the-handler-function

```typescript
    const res = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${RESEND_API_KEY}`
    },
    body: JSON.stringify({
      from: 'onboarding@resend.dev',
     to: 'xxxxx@gmail.com',
      subject: 'hello world',
      html: '<strong>it works!</strong>'
    })
  });
```
ここでの主な作業は2つです。
1. `xxxxx@gmail.com`の部分はResendに登録した際のアドレスに変更
1. RESEND_API_KEYについてsecretセクションから値を設定
    - Key：`RESEND_API_KEY`
    - Value：Resendのコンソールで作成したAPI Keyの値
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/acdb0b38-58b5-428b-a817-8f90e2370e00.png)


**ターミナルから関数を直接実行してメール送信する、といったことは実際にはない**
ので、もう少し現実に近い形を作っていきます。

以下2つに取り組んでみます。
1. DBテーブルの挿入・更新をトリガーにメール送信
1. cronを用いて、特定のパターンでメール送信

### DBテーブルの挿入・更新をトリガーにメール送信
以下作業を行います。
1. DBテーブルを作成
1. WebHooksを作成

#### DBテーブルを作成
なんでもいいので、簡単なテーブルを作成します。
```sql
CREATE TABLE IF NOT EXISTS sample (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title TEXT NOT NULL
);
```

#### WebHooksを作成
テーブルのデータ挿入・更新時に、Edge Functionsを実行するように設定します。

https://supabase.com/docs/guides/database/webhooks

ダッシュボードのIntegrationsから、WebHookを作成します。（プロジェクトで有効化されていない場合は、有効化を実施してから）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/479fc119-ffd8-4a28-96d4-b3c910a319fb.png)

**設定作業1**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/cd3e670b-6672-495b-bfc2-d396f092c8dd.png)
Table：先ほど作成したテーブル名を選択
Events：データが`INSERT`された場合にメール送信（一番上のみチェック）

**設定作業2**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/1e40a3cd-326f-4f3d-a4d1-b8df0dd3addf.png)

- `Webhook configuration`：Edge Functionsを選択
- `Edge Function`：先ほど作成した関数を選択
- HTTP Headers：以下画像のプルダウンで、上を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/2521912e-d58b-48cf-bef5-6a591b5927b5.png)

これでsampleテーブルにデータを投入してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/3e5feaa5-288b-4f17-8256-e21161384842.png)

メールが届きました！成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/7061b4de-2c08-48eb-8980-d59f88404d6d.png)

あとは利用している言語に応じて、Supabaseのレコード追加をする実装を加えれば、
コードを通じて、メール通知まで実装ができるようになります。

続いてcronを用いた方をすすめていきます。

### cronを用いて、特定のパターンでメール送信
https://supabase.com/docs/guides/cron/quickstart

上記のページの内容を参考にしています。
cronを定義し、Edge Functionsと紐づけます。

DashBoardのIntegrationsのページから設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/d1db500f-7bd5-48ac-a215-f20697b22b96.png)

プロジェクトで利用できる状態にするため、`Enable pg_cron`をクリックして有効化をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/7391dc38-ae3a-450c-9825-7b67da34e780.png)

Jobタブを表示して、Job作成に進みます。
以降は、公式ドキュメントの画像通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/956dc6e6-e4a6-4199-8b71-aca5277b4651.png)

赤枠で囲った箇所をクリックすると、自動でcronの設定が更新されます。
ありがたいですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/d9ee58fd-5c6e-45d3-a061-a58d26497dad.png)

ここで、メールを送信する処理を記載したEdge Functionsを選択します。
Header情報の設定は、データベースの挿入をトリガーにしたときと同じです。
これでメールを特定の時間や曜日に送信する機能を作ることができました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/b18c657c-4f0c-4d9c-8ed9-4ec0b6eef8cd.png)

作成したJob画面から、次いつ実行されるか確認できるので、その時間が過ぎたらメール送信できているか確認します。
受信を確認できたら成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345414/c248ed3c-4934-435a-b8aa-86b1f999b44f.png)

## まとめと今後
SupabaseとResendを用いたメール送信機能の構築を実施してみました。

今回はSupabaseを用いましたが、コードからわかる通り、ResendのAPIキーさえあれば、
Vercelなどにデプロイしたアプリケーションからも簡単に実現することができます。

今後の作業として、
ドメインを設定し、Resendで登録したメールアドレス以外にも送信できるように進めていきたいと思います。