Twilio Serverless CLI V.2.x

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズの Twilio事業部エバンジェリストの高橋です。

今回は、先日メジャーアップデートされた Serverless CLI V.2.x について解説していきます。
これは、Twilio CLI の Serverless プラグインの最新バージョンになります。
Twilio Functions の Node.js バージョンが 12.x になり、既存のバージョンではデプロイ時に`--runtime=node12`の指定が必須になったのですが、今回の V.2.x では標準で 12.x に対応してくれています。
これ以外にもかなり重要な変更がされているようなので、Twilio CLI でサーバーレス開発をされる方はアップグレードは必須かと思います。

# インストール＆アップグレード

なにはともあれ、まずは最新版の Serverless CLI をインストールしてみましょう。
現在インストールされているバージョンを調べます。

`twilio plugins`と入力すると、現在インストールされているプラグインの一覧とそれぞれのバージョンが表示されます。

```sh
twilio plugins
@twilio-labs/plugin-flex 4.9.0-beta.0 (beta)
@twilio-labs/plugin-rtc 0.8.2
@twilio-labs/plugin-serverless 1.9.0
@twilio-labs/plugin-signal2020 1.0.5
@twilio-labs/plugin-token 3.2.0
@twilio-labs/plugin-watch 2.0.3
```
この例では、1.9.0がインストールされているので、これは古いバージョンです。

すでに以前のバージョンがインストールされている方は、以下のコマンドで古いバージョンを削除します。

```sh
twilio plugins:remove @twilio-labs/plugin-serverless
Uninstalling @twilio-labs/plugin-serverless... done
```

ではいよいよ最新バージョンをインストールしましょう。

```sh
twilio plugins:install @twilio-labs/plugin-serverless@latest
Installing plugin @twilio-labs/plugin-serverless... installed v2.0.2
```

本記事の執筆時点での最新バージョンは、2.0.2 のようです。
もし下記のようなエラーが出ちゃった方は、メッセージにある通り`Node.js`のバージョンが古いようです。僕は12.8.0でエラーになりました。

```sh
error twilio-run@3.0.1: The engine "node" is incompatible with this module. Expected version ">=12.22.1". Got "12.8.0"
error Found incompatible module.
Installing plugin @twilio-labs/plugin-serverless... failed
```
この場合、`Node.js`のバージョンを12.22.1以上にあげてください。
ちなみに僕は`Node.js`のバージョン管理に [n](https://github.com/tj/n) を使っています。複数のバージョンを切り替えたりするのが簡単なのでおすすめです。

# 変更点の解説

では、今回のバージョンアップによって変更された点を見ていきましょう。

## コマンド

まずは`serverless`コマンドに変わりがないかを確認します。

```sh
twilio serverless:
serverless:activate        -- Promotes an existing deployment to a new environment
serverless:deploy          -- Deploys existing functions and assets to Twilio
serverless:init            -- Creates a new Twilio Function project
serverless:list            -- List existing services, environments, variables, deployments for your Twilio Serverless Account
serverless:list-templates  -- Lists the available Twilio Function templates
serverless:new             -- Creates a new Twilio Function based on an existing template
serverless:start           -- Starts local Twilio Functions development server
```

どうもコマンド自体は変わっていない様子ですね。
ちなみに、serverless コマンドには一覧には出てこないコマンドもあります。

今回のバージョンアップより前からあるのが、
`twilio serverless:logs` [ドキュメント](https://www.twilio.com/docs/runtime/functions-assets-api/api/logs)
`twilio serverless:promote` [ドキュメント](https://www.twilio.com/docs/labs/serverless-toolkit/deploying#replicating-an-existing-deployment)
です。気になる方はぜひドキュメントを見てくださいね。
特に`logs`はサーバー上でのログをリアルタイムで見ることができるので超おすすめです。

## 自動生成されるファイル

では次にプロジェクトを一つ作ってみましょう。

```sh
twilio serverless:init v2test
 » Could not get credentials for profile "MainAccount".
 » To reconfigure the profile, run: twilio profiles:create
 ›   Error: EEXIT: 1
 ›   Code: EEXIT
```

おっと、プロファイルがないとか言われちゃいましたね。きっと`Node.js`のバージョンを上げたからでしょうか。
プロファイルを作り直してもうまくいかないので、ここは別のワークアラウンドを実行します。

```sh
export TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
twilio serverless:init v2test --import-credentials
✔ Creating project directory
✔ Creating project directories and files
✔ Downloading .gitignore file
✔ Installing dependencies
╭──────────────────────────────────────────────────────────────────────────────╮
│                                                                              │
│   Success!                                                                   │
│                                                                              │
│   Created v2test at /Users/katsumi/Documents/workspace/twilioCLI             │
│                                                                              │
│   Inside that directory, you can run the following command:                  │
│                                                                              │
│   npm start                                                                  │
│     Serves all functions in the ./functions subdirectory and assets in the   │
│     ./assets directory                                                       │
│                                                                              │
│   Get started by running:                                                    │
│                                                                              │
│   cd v2test                                                                  │
│   npm start                                                                  │
│                                                                              │
╰──────────────────────────────────────────────────────────────────────────────╯
```

うまくいきました。これは、プロジェクト作成時にプロファイルを見るのではなく、環境変数に`TWILIO_ACCOUNT_SID`と`TWILIO_AUTH_TOKEN`を記憶させて、`--import-credentials`オプションで読み込む方法です。これだとプロファイルを読みにいかないのでとりあえずは先にすすみます。

では、作成されたファイルを確認しましょう。

```sh
cd v2test
tree . --filelimit 100 -a
.
├── .env
├── .gitignore
├── .nvmrc
├── .twilioserverlessrc
├── assets
│   ├── index.html
│   ├── message.private.js
│   └── style.css
├── functions
│   ├── hello-world.js
│   ├── private-message.js
│   └── sms
│       └── reply.protected.js
├── node_modules [363 entries exceeds filelimit, not opening dir]
├── package-lock.json
└── package.json

4 directories, 12 files
```

### .env

今回のバージョンアップで変わった点の1つ目が、`.env`ファイルです。
以前のバージョンでは、`.env`ファイル内はこんな感じでした。

```text:.env
ACCOUNT_SID=SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

ACCOUNT_SIDには、本来の AccountSid ではなく、プロファイル作成時に自動で生成された API Key が指定されていて、かつ AUTH_TOKEN には、API Secret が指定されていました。パラメータ名と内容が一致しない、とても気持ちわるい感じだったし、API Key と API Secret が書かれちゃっているのでユーザの意図せず公開されてしまうリスクがありました。
これが、今回のバージョンで以下のように変わりました。

```text:.env
ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
AUTH_TOKEN=
```

本来の AccountSid が入るようになり、かつ AuthToken はパラメータのみとなります。気持ち悪さもなくなり、公開リスクもなくなりますね。AUTH_TOKEN パラメータは特に空欄でもデプロイはできます。
もちろん従来のバージョン同様、`.gitignore`ファイル内には、`.env`の記述があるのでこのまま GitHub にあげても`.env`ファイルは除外されます。

### .twilioserverlessrc

今回のバージョンでプロジェクトを作成すると、以前のバージョンではなかった`.twilioserverlessrc`というファイルができます。
中身は以下のような JSON5 ファイルです。

```json5
{
	"commands": {},
	"environments": {},
	"projects": {},
	// "assets": true 	/* Upload assets. Can be turned off with --no-assets */,
	// "assetsFolder": null 	/* Specific folder name to be used for static assets */,
	// "buildSid": null 	/* An existing Build SID to deploy to the new environment */,
	// "createEnvironment": false 	/* Creates environment if it couldn't find it. */,
	// "cwd": null 	/* Sets the directory of your existing Serverless project. Defaults to current directory */,
	// "detailedLogs": false 	/* Toggles detailed request logging by showing request body and query params */,
	// "edge": null 	/* Twilio API Region */,
	// "env": null 	/* Path to .env file for environment variables that should be installed */,
	// "environment": "dev" 	/* The environment name (domain suffix) you want to use for your deployment */,
	// "extendedOutput": false 	/* Show an extended set of properties on the output */,
	// "force": false 	/* Will run deployment in force mode. Can be dangerous. */,
	// "forkProcess": true 	/* Disable forking function processes to emulate production environment */,
	// "functionSid": null 	/* Specific Function SID to retrieve logs for */,
	// "functions": true 	/* Upload functions. Can be turned off with --no-functions */,
	// "functionsFolder": null 	/* Specific folder name to be used for static functions */,
	// "inspect": null 	/* Enables Node.js debugging protocol */,
	// "inspectBrk": null 	/* Enables Node.js debugging protocol, stops execution until debugger is attached */,
	// "legacyMode": false 	/* Enables legacy mode, it will prefix your asset paths with /assets */,
	// "live": true 	/* Always serve from the current functions (no caching) */,
	// "loadLocalEnv": false 	/* Includes the local environment variables */,
	// "loadSystemEnv": false 	/* Uses system environment variables as fallback for variables specified in your .env file. Needs to be used with --env explicitly specified. */,
	// "logCacheSize": null 	/* Tailing the log endpoint will cache previously seen entries to avoid duplicates. The cache is topped at a maximum of 1000 by default. This option can change that. */,
	// "logLevel": "info" 	/* Level of logging messages. */,
	// "logs": true 	/* Toggles request logging */,
	// "ngrok": null 	/* Uses ngrok to create a public url. Pass a string to set the subdomain (requires a paid-for ngrok account). */,
	// "outputFormat": "" 	/* Output the log in a different format */,
	// "overrideExistingProject": false 	/* Deploys Serverless project to existing service if a naming conflict has been found. */,
	// "port": "3000" 	/* Override default port of 3000 */,
	// "production": false 	/* Promote build to the production environment (no domain suffix). Overrides environment flag */,
	// "properties": null 	/* Specify the output properties you want to see. Works best on single types */,
	// "region": null 	/* Twilio API Region */,
	// "runtime": null 	/* The version of Node.js to deploy the build to. (node10 or node12) */,
	// "serviceName": null 	/* Overrides the name of the Serverless project. Default: the name field in your package.json */,
	// "serviceSid": null 	/* SID of the Twilio Serverless Service to deploy to */,
	// "sourceEnvironment": null 	/* SID or suffix of an existing environment you want to deploy from. */,
	// "tail": false 	/* Continuously stream the logs */,
	// "template": null 	/* undefined */,
}
```

どうやら serverless コマンドで指定することが可能なオプションパラメータを予め指定しておけるファイルのようです。毎回オプションフラグをつけていた方は、こちらの JSON でデフォルト値を設定しておくと便利です。

### 環境変数の切り替え

`.twilioserverlessrc`の`environments`は環境変数の切り替えに使えるようです。

たとえば、

```json5
{
	"commands": {},
  	"environments": { 
		"dev": { "env": ".env"},
		"stage": { "env": ".env.stage"},
    	"*": { "env": ".env.production" }
  	},
	"projects": {},
（省略）
}
```

のように記載しておき、`.env`ファイルと`.env.stage`ファイル、`.env.production`ファイルを用意しておきます。
`.env.stage`には、ステージング用の環境変数を、`.env.production`には、本番環境用の環境変数を設定しておきます。

ステージング用にデプロイするには、

```sh
twilio serverless:deploy --environment=stage
```

を実行します。
一方、本番環境をデプロイするには、

```sh
twilio serverless:deploy --production
```

とすることで、`.env.production`を読み込んでデプロイしてくれます。`--production`オプションをつけると、生成される Functions の URL も本番用に変わりますので、一つのプロジェクトで本番環境と開発環境の両方を管理することができます。

:::note warn
✍🏻 <font color='red'>**重要**</font>
プロジェクト生成時に自動的に作られる`.gitignore`には、`.env`、`.env.test`、`.env.production`の3つは予め指定がされているので、これら以外の名前で環境変数ファイルを作成する場合は、忘れずに`.gitignore`に記載するようにしましょう。
:::

ではさっそく、今回のバージョンでプロジェクトを本番環境でデプロイしてみます。

```sh:本番環境でのデプロイ結果
twilio serverless:deploy --production

Deploying functions & assets to the Twilio Runtime

Username	ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Password	af5b****************************
Service SID	ZS4f05e4c74ae009a1f3a6c829b3b3d794
Environment
Root Directory	/Users/katsumi/Documents/workspace/twilioCLI/v2test
Dependencies	twilio
Env Variables	MESSAGE
Runtime		undefined

✔ Serverless project successfully deployed

Deployment Details
Domain: v2test-xxxx.twil.io
Service:
   v2test (ZS4f05e4c74ae009a1f3a6c829b3b3d794)
Environment:
    (ZE4c9815580e805c44d4b29e5cdef0a88c)
Build SID:
   ZB2a110d868eb20e9253ae18f0e547225f
Runtime:
   node12
View Live Logs:
   Open the Twilio Console
Functions:
   [protected] https://v2test-xxxx.twil.io/sms/reply
   https://v2test-xxxx.twil.io/hello-world
   https://v2test-xxxx.twil.io/private-message
Assets:
   [private] Runtime.getAssets()['/message.js']
   https://v2test-xxxx.twil.io/index.html
   https://v2test-xxxx.twil.io/style.css
```

ちなみに開発環境でデプロイをすると、Functions の URL は以下のように本番用とは異なるものが生成されます。

```sh:開発環境でのデプロイ結果
Functions:
   [protected] https://v2test-6075-dev.twil.io/sms/reply
   https://v2test-6075-dev.twil.io/hello-world
   https://v2test-6075-dev.twil.io/private-message
```

### .twiliodeployinfo

以前のバージョンでは一度デプロイをすると`.twilio-functions`というファイルが生成されましたが、今回のバージョンでは生成されることはありません。その代わりに、`.twiliodeployinfo`というファイルが生成されます。
内容は以下の通りです。

```json:.twiliodeployinfo
{
	"ACxxxxxxxxxxxxxxxxxxxxxxxxxxxx": {
		"serviceSid": "ZS4f05e4c74ae009a1f3a6c829b3b3d794",
		"latestBuild": "ZB85bd11cdedb365e4c8a048f25c444521"
	}
}
```

このファイルは、GitHubなどのレポジトリには含めないように推奨されています。ただし、セキュリティ的な問題というよりは複数人での開発などを意識しての推奨です。`.gitignore`にはこのファイルの指定がないので、必要に応じて入れておくとよいでしょう。

### その他

他にも、ローカル実行時の挙動などにも変更がされているようです。
ローカル実行とは、`twilio serverless:start`によって、ローカル環境上でFunctionsのエミュレーションを行うことで、サーバーにデプロイすることなく開発ができる強力な機能です。
従来の問題として、`Callback(...)`が呼ばれても、別のプロセスで実行中のプログラムは停止しなかったのが、`Callback(...)`が呼ばれたタイミングですべてのプロセスが終了するようになります。たとえば、非同期で実行した処理があるにも関わらず、`Callback(...)`が呼ばれても、従来であれば非同期の処理はすべて実行されるため、テスト環境では動くのに本番環境にデプロイすると動かないといったトラブルが想定されました。
今回のバージョンで、よりサーバー上での動作に近いかたちで動くことになりますので、このような問題が減ることが期待されます。

# まとめ

今回のアップデートは、v.1.x系からv.2.xへのメジャーアップデートとなりますので、かなり色々と強化されています。
身近なところでは、Node12の標準サポートが大きいですが、これだけでなく複数人での開発をよりしやすくするためのしくみもあるので、ますます開発が楽になると思います。

それにしても、Nodeのバージョンをあげるとプロファイルが読めなくなるのはなんとかしたい・・・。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング（SMS /チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2015 Hall of Doers<br />2019 Twilio Champions