API GatewayからLambdaを呼び出すときにevent.bodyをオブジェクトで受け取る

# はじめに

AWS API GatewayからLambdaを呼び出すこと、よくあると思います。
Lambdaでは`event.body`でリクエストボディを取得できますが、
デフォルトではこの`body`は文字列なので、
`Content-Type:application/json`の場合も「JSON文字列」になります。
そのため、一度JSONをパースしてあげないといけません。

たとえばNode.jsの場合はこんな感じ。

```js:index.js
exports.handler = async (event) => {
    const body = JSON.parse(event.body);
    console.log('api received!', body);

    const response = {
        statusCode: 200,
        body: JSON.stringify([body.hoge, body.fuga, body.piyo]),
    };
    return response;
};
```

コード側で`JSON.parse()`すること自体は大した手間ではないのですが、
面倒くさいのがテストです。

Lambdaの「テストイベントの設定」で、こんな感じで書きたいわけですよ。

```json
{
  "body": {
    "hoge": 1,
    "fuga": 2,
    "piyo": 3
  }
}
```


しかしながら、これは「JSON文字列」ではなく「JSONオブジェクト」なので、
API Gatewayの`event.body`とは互換性がありません。
こうしてやらなければならない。。。

```json
{
  "body": "{\"hoge\":1,\"fuga\":2,\"piyo\":3}"
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/30059616-d4b2-49fe-2007-c313b26fc28e.png)

これはねぇ、、、辛いですよね。
ダブルクォートのエスケープの嵐で、サクッと手書きで書くことは無理です。

# 解決方法

API Gatewayの[「マッピングテンプレート」](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/rest-api-data-transformations.html)という機能を使うと、
ちゃんとJSONをオブジェクトにしてからLambdaに飛ばすことができます。

## Lambda側のトリガー設定から新規REST APIを作成

* 検証用に、ごくごくシンプルな設定でAPIを作成
  * 実運用では「オープン」は危険なので要注意
* HTTP APIではマッピングテンプレートが使えないので、REST APIにする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/8c5a21c5-bbb9-05b9-74d2-0b1e7097a00c.png)


## API Gatewayのリソース設定

* API Gatewayのリソース設定に飛ぶ
* 「ANY」メソッドの「統合リクエスト」を開く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/176d26ec-ef66-832a-3cc9-737a4861bf27.png)

* Lambdaから作ったAPIは「Lambdaプロキシ統合の使用」がチェックされているので、このチェックを外す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/64ae101d-ded4-c8c6-b086-d443b1f6c210.png)

* チェックを外すとすると詳細な設定項目が現れるので、「マッピングテンプレート」を開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/5f0ee125-22c2-3d0c-74d6-9411d64e62ce.png)

## マッピングテンプレートの設定

* リクエスト本文のパススルー：「テンプレートが定義されていない場合 (推奨)」を選択
* マッピングテンプレートの追加：「application/json」を入力
* テンプレートの生成：「メソッドリクエストのパススルー」を選択すると、いい感じのプリセットが入る
    * 書式が独特なので、一から書くのが大変なのですよね。。このプリセットがとってもありがたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/345aad5f-1fff-764f-cfb3-24423ff05bbb.png)

* ほとんどプリセットのままで良いのだけど、`body-json`はcamelCaseの`bodyJson`にしておくと、JavaScriptでは扱いやすい
    * もしくはシンプルに`body`でもOK
* 編集したら「保存」

```diff

 ##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
 #set($allParams = $input.params())
 {
-"body-json" : $input.json('$'),
+"bodyJson" : $input.json('$'),
 "params" : {
 #foreach($type in $allParams.keySet())
     #set($params = $allParams.get($type))
```

## APIのデプロイ

* アクションから「APIのデプロイ」を選択
* ステージ「default」を選んで、説明は書きたければ書いて、「デプロイ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/9e2c9e09-7e04-1f0d-9578-23794c78bf51.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/6c8c4463-eb2b-b962-a5da-ef1a55dc9aba.png)


## Lambdaの修正版と実行結果

「Lambdaプロキシ統合の使用」が外れたことで、
レスポンスの作り方も変わります。
`statusCode`はAPI Gateway側で定義するので、
Lambdaではシンプルに、正常系のレスポンスをそのまま`return`するだけ。

```js:index.js
exports.handler = async (event) => {
    const body = event.bodyJson;
    console.log('api received!', body);

    return [body.hoge, body.fuga, body.piyo];
};
```

Lambdaテストイベント

```json
{
  "bodyJson": {
    "hoge": 1,
    "fuga": 2,
    "piyo": 3
  }
}
```

PCターミナルからcurlでAPI叩いてテスト

```shell-session
$ curl -X POST https://example.amazonaws.com/default/mappingTemplateTest -H 'Content-Type:application/json' -d '{"hoge":1,"fuga":2,"piyo":3}'
[1,2,3]                                                  
```

いい感じですね！

# 参考サイト

AWS公式
* [REST API のデータ変換の設定](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/rest-api-data-transformations.html)
* [API Gateway で Lambda プロキシ統合を設定する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html)

ブログ・Qiita
* [[AWS]API Gatewayの本文マッピングテンプレートを理解する](https://dev.classmethod.jp/articles/api-gateway-mapping-template/)
* [API Gateway + Lambda にFormからPOSTする時のマッピングテンプレートを作成しました](https://dev.classmethod.jp/articles/sugano-013-api-gateway/)
* [API Gatewayのマッピングテンプレートの設定例](https://qiita.com/tamura_CD/items/ca8e531f74ea5b82a5b7)

ではまた～。