Hexabaseのアクション拡張プログラムを使って、サーバーサイドでスクリプトを実行する

[Hexabase](https://www.hexabase.com/)はエンタープライズ向けのBaaS（Backend as a Service）を提供しています。主な機能としてはデータベース、ファイルストレージ、認証、リアルタイム通信などになりますが、それだけで全ての開発要件を満たせる訳ではありません。

そこで、足りない機能を開発し、コードをサーバーサイドで実行できるアクション拡張プログラムが用意されています。いわゆるFaaS（Function as a Service）のようにして、スケーラブルにコードを実行できます。

今回はそのアクション拡張プログラムの使い方と、[blastengine](https://blastengine.jp/)を使ってメール送信を行う（ネットワーク通信を行う）方法を解説します。

## 開発者機能を有効にしてください

アクション拡張プログラムを使う場合には、Hexabaseの個人設定で `開発者向け機能` を有効にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/02e868fa-a23f-011c-50df-9d8f49eed241.png)


そうすると、アプリケーションの設定で `プログラム拡張` メニューが追加されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/7ce3d4f9-202a-b037-4759-8bc7d8b2b07d.png)


## 定数の設定

拡張を書く際には、APIキーや認証情報などコードには直接書きたくない情報もあるでしょう。そこで使えるのが定数の登録です。 

これは設定した定数名が、コードにある `{定数名}` と書かれた部分と置き換わる機能になります。環境変数とは異なるので注意してください。

今回は以下の2つの定数を定義しました。

| 定数名 | 意味 |
| --- | --- |
| BE_USERID | blastengineのユーザーID |
| BE_APIKEY | blastengineのAPIキー |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/59f57459-1e3e-76a6-a3eb-56a6a4e438bc.png)


## コードについて

続いて関数タブに移動して、追加をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/df70a42f-0d8d-714f-147c-fab21b026f78.png)


そうするとモーダルウィンドウが開いて、エディタが開きます。左側が関数に送られてくるデータ、右側がエディタになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/f2db6e0c-0740-44d0-1444-cff4ae34acf3.png)


まず関数名と関数IDを決めてください。基本的に同じでも大丈夫ですが、関数IDは英数字での指定になります。今回は `sendMail` としています。

### データについて

基本的に送られてくるのは、関数を実行したユーザー情報などになります。各キーの意味は以下のようになります。

| キー | 意味 |
| --- | --- |
| u_id | ユーザーID |
| user_name | ユーザー名 |
| email | ユーザーのメールアドレス |
| w_id | ワークスペースID |
| ws_key | WeSocketキー |
| p_id | プロジェクトID |
| p_display_id | プロジェクト表示名 |

そして、APIで呼び出す際に独自のパラメータを付与した場合、すべて `params` キーの中にデータが入ってきます。

全体としては以下のようになります。

```json
{
  "u_id": "xyz",
  "user_name": "USER_NAME",
  "email": "you@example.com",
  "w_id": "0123456789",
  "ws_key": "",
  "p_id": "abcdefghijk",
  "p_display_id": "PROJECT_NAME",
  "params": { // 独自のパラメータ
    "a": "b"
  }
}
```

## コードの書き方

アクション拡張プログラムでは必ず `main` 関数が呼ばれます。そして、1つ目の引数に上記JSONオブジェクトが入ります。以下が基本形です。

```jsx
async function main(data) {
  // 呼ばれた際のコード
}
```

### コードで使えるオブジェクト

アクション拡張プログラムでは `require` は使えません。あらかじめ下記グローバルオブジェクトが用意されていますので、ご利用ください。

- process
- request
- lodash
- crypto
- fs
- FormData
- Readable
- Writable
- ftp
- jwt
- axios
- sendgridMail
- httpCallAsync

### サンプル

Hexabaseでも幾つかのサンプルコードを用意しています。その中にSendGridを使ってメール送信を行うコードがありますが、実装は `sendgridMail` を使って隠蔽化されています。そこでシンプルなAPI呼び出しを行うコードをblastengineを使って実装してみます。

今回はrequestを使っています。requestは [request/request: 🏊🏾 Simplified HTTP request client.](https://github.com/request/request#forms) になります。

注意点としては定数の使い方でしょう。環境変数ではなく `'{BE_USERID}{BE_APIKEY}'` のように書いて、該当部分が設定した定数の内容に置き換わります。コードの内容は[blastengineを使ってNode.jsでメールを送信する - Qiita](https://qiita.com/goofmint/items/88d9019be54567c542be)を参考してください。blastengineではNode.js SDKも提供していますが、今回は素のAPIコールを行っています。

```jsx
async function main(data) {
    const str = '{BE_USERID}{BE_APIKEY}';
    const hashHex = crypto
        .createHash('sha256')
        .update(str, 'utf8')
        .digest('hex');
    const token = Buffer
        .from(hashHex.toLowerCase())
        .toString('base64');
    const text_part = `Hexabaseのアクションスクリプトです

実行ユーザー名 ${data.user_name}
プロジェクト名 ${data.p_display_id}
パラメーター
${JSON.stringify(data.params)}

`;
		const params = {
			from: {
					email: 'no-reply@example.jp',
					name: '管理者'
			},
			to: 'user@example.com',
			subject: 'Hexabase 更新通知',
			encode: 'UTF-8',
			text_part,
		};
    const res = await request
        .post({
            url: 'https://app.engn.jp/api/v1/deliveries/transaction',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
            },
            json: params,
        });
    return "OK";
}
```

## 呼び出し方

コードを書き終わったら、システムに組み込みます。REST APIから呼び出す場合には[ExecuteFunction | Hexabase API ガイド](https://apidoc.hexabase.com/docs/v0/functions/ExecuteFunction)を参照してください。

Flutter SDKでは以下のようにして呼び出します。

```dart
var client = Hexabase.instance;
var project = client.project();
project.id = 'YOUR_PROJECT_ID';
var result = await project.function('YOUR_FUNCTION_ID', params: {'a': 1, 'b': 2});
```

これで `result` には `OK` という文字列が返ってきて、メールが届きます。

![スクリーンショット 2023-01-27 17.17.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/155d947e-f166-c763-3653-369fb4a1f0cf.png)


## まとめ

BaaSだけで全ての要件が実現できない場合でも、拡張することでサーバーを極力用意せずにシステム開発ができます。

サーバーレスな業務システム開発を実現するために、ぜひ使ってください。

[株式会社 Hexabaseー法人向けクラウドシステム『Hexabase』ー](https://www.hexabase.com/)