Hexabaseとblastengineでメールマガジンを送信する

Hexabaseはエンタープライズ企業向けのBaaS（Backend as a Service）を提供しています。認証やデータストア（データベース）、ファイルストレージ、FaaS（Function as a Service）といった機能があります。

今回はそんなHexabaseとblastengineを使って、メールマガジンを作成します。

## コード

コードは [blastengineMania/hexabase-mailmagazine](https://github.com/blastengineMania/hexabase-mailmagazine) にアップしてあります。実装時の参考にしてください。

## アーキテクチャ

今回のアーキテクチャは以下のようになります。Hexabaseのデータストアに送信するメールのテンプレート（件名や本文など）、メールアドレスの2つのテーブルを用意します。

メールアドレスのテーブルにあるデータを使って、メールマガジンを送信します。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/c22d8c4b-11a1-0e18-4fee-0788031863e2.png)


## blastengineの準備

### ユーザ登録する

blastengineにユーザ登録します。管理画面に入るためのユーザID、パスワードが手に入るので、ログインします（ユーザIDは後で使います）。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/dd84066c-7735-d55d-9e53-c782974fca71.png)

### 送信元ドメインのSPFを設定する

送信元として利用するドメイン（自分で持っているもの）の設定をします。これは任意のドメイン管理サービスで設定できますが、TXTレコードに以下のSPFを追加します。

```
txt @ v=spf1 include:spf.besender.jp ~all
```

### APIキーを取得する

ログイン後、管理画面の右上にある設定メニューに移動します。

![https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/47b2b2ac-2e41-77fe-d84f-0ae0b7541f36.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/47b2b2ac-2e41-77fe-d84f-0ae0b7541f36.jpeg)

そして設定の中で、APIキーを取得します。

![https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2b5ca124-3020-1d39-84e4-911cd4db27e2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2b5ca124-3020-1d39-84e4-911cd4db27e2.png)

## Hexabaseの準備

まずHexabase側の準備です。以下を順番に作成します。

- ワークスペース
任意の名前でOKです。ワークスペースIDはメモしておきます。
- プロジェクト
任意の名前でOKです。プロジェクトIDはメモしておきます。
- データストア
メールテンプレートテーブル（Mail）とメールアドレステーブル（Email）を作成します。

### データストアの設計

メールテンプレートテーブル（Mail）は、以下の項目を持っています。ステータスはデフォルトで「送信前」、利用した後は「送信後」に更新されます。

| 項目 | 型 | 英語名 & Display ID |
| --- | --- | --- |
| 件名 | テキスト | subject |
| 本文 | テキスト | body |
| 送信元名 | テキスト | fromName |
| 送信元メールアドレス | テキスト | fromEmail |
| 配信ID | 数値 | deliveryId |
| ステータス | ステータス | Status |

メールアドレステーブル（Email）は、以下の項目を持っています。ステータスはデフォルトで「送信可」として、送信に失敗した場合には「送信不可」ステータスになります。

| 項目 | 型 | 英語名 & Display ID |
| --- | --- | --- |
| メールアドレス | テキスト | mailaddress |
| 送信エラー | テキスト | sendError |
| ステータス | ステータス | Status |

![Untitled.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/d9a030bb-f461-5be5-2c78-dbadd07ba7d5.jpeg)


### ステータスの作成

ステータスはサンプルで登録されているレコードを編集する中で作成・編集できます。

メールテーブルはデフォルトが送信前、もう一つが送信後になります。メールアドレステーブルはデフォルトが送信可、もう一つが送信不可になります。

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/78f22714-3f61-f569-f1a1-8de87c045519.png)


### パブリックトークンの準備

Hexabaseでは認証して利用するのが基本ですが、お問い合わせの場合は認証は使わないでしょう。そこで、特定のユーザー情報に紐付くパブリックトークンを生成します。なお、執筆現在ではワークスペースごとの設定が必要です（要問い合わせ）。

![Untitled 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2d7b4f55-0884-b079-2c7c-1ee46e31719a.png)


## 実装について

### プロジェクトのフォルダ作成とNode.jsプロジェクトの初期化

今回は `hexabase-mail` とします。以下はこのフォルダの中で処理します。

```
mkdir hexabase-mail
cd hexabase-mail
npm init -y
```

### ライブラリのインストール

必要なライブラリをインストールします。

- @hexabase/hexabase-js
- blastengine
- dotenv
- ts-node
- typescript

```
npm i @hexabase/hexabase-js
npm i blastengine
npm i dotenv
npm i ts-node
npm i typescript
```

TypeScriptプロジェクトとして初期化します。

```
npx tsc --init
```

以下は index.ts での実装内容です。

### 環境変数の用意

`.env` を作成して、環境変数を用意します。

```bash
BLASTENGINE_USER_ID=blastengineのユーザーID
BLASTENGINE_API_KEY=blastengineのAPIキー
HEXABASE_PUBLIC_TOKEN=Hexabaseのパブリックトークン
HEXABASE_WORKSPACE_ID=Hexabaseの対象ワークスペースID
HEXABASE_PROJECT_ID=Hexabaseの対象プロジェクトID
HEXABASE_DATASTORE_MAIL_ID=Hexabaseのデータストア（メールテンプレート）のID（Mailなど）
HEXABASE_DATASTORE_EMAIL_ID=Hexabaseのデータストア（メールアドレス）のID（Emailなど）
```

### ライブラリのインポート

必要なライブラリ（blastengine、HexabaseのSDK）、dotenvを読み込みます。

```tsx
import { BlastEngine, Mail } from 'blastengine';
import { HexabaseClient } from '@hexabase/hexabase-js';
import 'dotenv/config';
```

### blastengine、Hexabase SDKの初期化

blastengine、Hexabase SDKを初期化します。

```tsx
new BlastEngine(process.env.BLASTENGINE_USER_ID!, process.env.BLASTENGINE_API_KEY!);
const client = new HexabaseClient();
```

### メインコード

非同期処理が多いので、処理全体をasync/awaitで処理します。

```tsx
(async () => {
	// 以下はこの中に書きます
})();
```

### メールのテンプレートを取得する

引数で指定されたメールのIDを使って、Hexabaseからテンプレートを取得します。

```tsx
await client.setToken(process.env.HEXABASE_PUBLIC_TOKEN!);
const mailId = process.argv[2];
if (!mailId) throw new Error('メールIDが指定されていません');
await client.setWorkspace(process.env.HEXABASE_WORKSPACE_ID!);
const query = client.query(process.env.HEXABASE_PROJECT_ID!);
// メールの本文と件名
const res = await query
	.from(process.env.HEXABASE_DATASTORE_MAIL_ID!)
	.where([
		query.condition.equalTo('i_id', mailId),
		query.condition.equalTo('Status', 'before')
	])
	.limit(1)
	.select('*');
const template = res[0];
if (!template) throw new Error(`${mailId}は存在しません`);
```

### メールアドレスをセットする

Hexabaseにあるメールアドレス一覧を取得し、blastengineのメールオブジェクトにセットします。ステータスはメール送信が有効になっているものだけに指定します。

```tsx
const emails = await query
	.from(process.env.HEXABASE_DATASTORE_EMAIL_ID!)
	.where(
		query.condition.equalTo('Status', 'enable')
	)
	.limit(0)
	.select('mailaddress');
const mail = new Mail;
for (const email of emails) {
	mail.addTo(email.get<string>('mailaddress')!);
}
```

### テンプレートの内容をセットする

メールオブジェクトに送信元、件名、本文をセットします。

```tsx
mail
	.setFrom(template.get<string>('fromEmail')!, template.get<string>('fromName')!)
	.setText(template.get<string>('body')!)
	.setSubject(template.get<string>('subject')!)
```

### メールを送信する

メールを送信し、結果として得られる配信ID（デリバリーID）をHexabaseに反映します。保存した後、ステータスを送信完了 `Sent` に更新します。

```tsx
await mail.send();
await template
	.set('deliveryId', mail.deliveryId)
	.save();
await template.execute('Sent');
```

## まとめ

今回はHexabaseと組み合わせて、メールマガジンを送信するコードを紹介しました。Hexabaseのデータストアにメールアドレスを保存しておくことで、メールマガジンの送信が簡単になります。

次回はメール送信の結果を受けて、エラーになったメールアドレスを更新するコードを紹介します。

[エンジニア向けメール配信システム「ブラストエンジン（blastengine）」](https://blastengine.jp/)