Clova公式SDK(Node.js)のAWS、Firebaseでの動かし方

# はじめに
[前回の記事(Clova公式SDK(Node.js)の使い方まとめ)](https://qiita.com/miso_develop/items/6b256c1e8757c0ace4a9)にて公式SDKの使い方を説明して、サーバでの動かし方も追記しますと書いたものの、ボリュームが大きかったので記事を分割しました。

AWS(Lombda)とFirebase(Functions)での動かし方をそれぞれ説明していきます。
どちらも公式SDKの[サンプル](https://github.com/line/clova-cek-sdk-nodejs#example)にあるようなexpressで動かすコードの最後に2～3行追加するだけでおっけーです。

* [AWS(Lambda)で動かしてみる](#awslambdaで動かしてみる)
* [Firebase(Functions)で動かしてみる](#firebasefunctionsで動かしてみる)
* [ForkされたSDKを使う](#forkされたsdkを使う)


# AWS(Lambda)で動かしてみる

## aws-serverless-expressを使う
公式SDKの[サンプル](https://github.com/line/clova-cek-sdk-nodejs#example)ではexpressで動かされており、できるだけソースをいじらずLambdaで動かせないかなと調べてみたところ、「[aws-serverless-express](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwjgk--LwLLcAhVHV7wKHck9CeoQFggoMAA&url=https%3A%2F%2Fgithub.com%2Fawslabs%2Faws-serverless-express&usg=AOvVaw1tCK-YCR4W53fGv2NXNP_D)」というのがありました。

まずはnpmインストール。

```
$ npm i aws-serverless-express
```

そして以下のようにちょこっといじるだけでexpressがLambdaで動きます。
**なお`app.post()`の第一引数を作成したLambda関数名にしておく必要があります。**

```js
// 公式サンプルの最後の方
...

const app = new express();
const clovaMiddleware = clova.Middleware({ applicationId: "YOUR_APPLICATION_ID" });
// Use `clovaMiddleware` if you want to verify signature and applicationId.
// Please note `applicationId` is required when using this middleware.
app.post('/clova', clovaMiddleware, clovaSkillHandler);

// Or you can simply use `bodyParser.json()` to accept any request without verifying, e.g.,
app.post('/clova', bodyParser.json(), clovaSkillHandler);

// 以下の3行を追加するだけ！
const awsServerlessExpress = require('aws-serverless-express');
const server = awsServerlessExpress.createServer(app);
exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context);
```

あとはソースを`node_modules`ごとzipに固めてLambdaにアップロードすればおっけーです。



## Lambda関数の作り方(Alexaスキル作成時との差異)
Alexaの場合はトリガーに「Alexa Skill Kit」を選択しますが、Clovaの場合はちろんそれは使えません。
じゃあどうするのかというと「トリガーの追加」から「API Gateway」を選択します。

![image](https://qiita-image-store.s3.amazonaws.com/0/207109/fdc92cbd-badb-6c5a-8cf4-dc97ff21bde3.png)
<br>

API Gateway選択後の操作を箇条書きしていきます。

* 「API」で「新規APIの作成」を選択
* 「セキュリティ」で「オープン」を選択
* 「追加」ボタンをおす

※Lambda上ではなくAPI Gateway上からですとより詳細な設定が行えます。
　特にLambda上ではメソッドが`ANY`に固定されてしまいますが、ここを`POST`のみにもできます。
　（@zono_0 さんのコメントより）

![image](https://qiita-image-store.s3.amazonaws.com/0/207109/25e683a1-62e5-ef28-d790-13a88d61d60a.png)
<br>

* 画面右上の「保存」ボタンをおす

![image](https://qiita-image-store.s3.amazonaws.com/0/207109/f8b2103e-cabe-7805-8747-259d20999732.png)
<br>

* 画面下側が更新されるので「詳細」を開く
* 「URLの呼び出し」のURLをコピー

![image](https://qiita-image-store.s3.amazonaws.com/0/207109/55ab002e-9444-d2d2-2c6a-0e9e4bcbe493.png)
<br>

* Clova Developer Centerの「サーバ設定」より「ExtensionサーバーのURL」に貼り付けて保存

![image](https://qiita-image-store.s3.amazonaws.com/0/207109/86c260ea-6baf-e54f-e771-c4d00294850f.png)
<br>

以上で公式SDKをLambdaで動かせます！



# Firebase(Functions)で動かしてみる
Firebase Functionsは[デフォルトでexpressを動かせます](https://firebase.google.com/docs/functions/http-events?hl=ja#using_existing_express_apps)。
ただコードの注意点としてはエンドポイントとなるURLの最後のパスを`exports`で指定します。
そのため、`app.post()`の第一引数は`'/'`としておく必要があります。
↓のコードの例ですと`https://xxx.xxx.xxx/clova/`の`clova`を`exports.clova`で指定しています。
あと、**Clova Developer Centerの「サーバ設定」にURLを貼るときはURLの最後に`/`を忘れずにつけて下さい。**

```js
// 公式サンプルの最後の方
...

const app = new express();
const clovaMiddleware = clova.Middleware({ applicationId: "YOUR_APPLICATION_ID" });
// Use `clovaMiddleware` if you want to verify signature and applicationId.
// Please note `applicationId` is required when using this middleware.
// app.post('/clova', clovaMiddleware, clovaSkillHandler);
app.post('/', clovaMiddleware, clovaSkillHandler);

// Or you can simply use `bodyParser.json()` to accept any request without verifying, e.g.,
// app.post('/clova', bodyParser.json(), clovaSkillHandler);
app.post('/', bodyParser.json(), clovaSkillHandler);

// 以下の2行を追加する！
const functions = require('firebase-functions');
exports.clova = functions.https.onRequest(app);
```

これで動く！！
…が、

## Firebaes FunctionsではES2016/ES2017が使えない
Firebase Functionsは現状Node.jsのバージョンが6となるためES2016/ES2017の構文（async/awaitとか）は使えません。
ES2015で書くか、Babelなりでトランスパイルの必要があります。

## 公式SDK内でES2017の記述がある？
`getSlots()`を使ったら以下のエラーが出ました。

```
Object.values is not a function 
 ```
 
`Object.values`はES2017の機能なので、Firebase Functionsでは動きません。
公式SDKはTypeScriptで書かれており、ES2015形式へのトランスパイルが行われているはずですが、そこでなんかおかしな事が起きてるのかな？（TypeScriptよくわかんない><）

## Firebaseは無料プランでおっけー
Firebaseは無料プランでは外部APIを叩けません。
じゃあClovaで使って大丈夫なの？と思いましたが、あくまでhttpリクエストを送って投げ返してるだけなので、ソース上で外部API叩いてなければ大丈夫です。



# ForkされたSDKを使う
@chibi929 さんが公式SDKをForkしてLambda、Firebaseで簡単に使えるよう拡張してくれています。
[chibi929/clova-cek-sdk-nodejs](https://github.com/chibi929/clova-cek-sdk-nodejs)

使い方はこちらの記事に。
[Clova SDK で作ったカスタムスキルを色んなサーバーにデプロイしてみる](https://qiita.com/chibi929/items/df450c91927d4d57ea56)

御本人に本家にプルリクしてもらうようお願いしたので、こちらがマージされたら更に使いやすくなりますね。