【これ検索するの何回目だよ】API Gateway + Lambdaプロキシ統合でもう躓きたくない

# はじめに
フロントエンドからAPI Gatewayを経由してLambdaを実行する時、毎回少しだけ躓きます、、、。
解決はするのですが、何度も同じことを経験しては忘れてを繰り返しているので教訓をまとめます！！！

# 教訓
1. **統合レスポンスをクライアントに返却する際、lambda → API Gatewayのレスポンスは正しい形式である必要がある！**
1. **ライブラリによってPOSTの仕方が異なるので注意するべし！！**
1. **フロントのリクエストボディはparseして利用！！！**

これだけです！あとは簡単！！
lambda統合機能最高！！！

# 動作確認

## 1. **統合レスポンスをクライアントに返却する際、lambda → API Gatewayのレスポンスは正しい形式である必要がある！！**

API Gatewayのメソッドテスト機能で、以下のようなエラーが発生することがあります。

![cap05.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/18e9ad13-88bb-5777-b36e-7f93def5c9f8.png)

![cap06.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/0346abee-e460-3eba-9c46-ba9424c37983.png)


```
Sat Aug 31 06:47:31 UTC 2024 : Execution failed due to configuration error: Malformed Lambda proxy response
Sat Aug 31 06:47:31 UTC 2024 : Method completed with status: 502
```

エラーメッセージを読むと、以下の記載があることが確認できます。
>Malformed Lambda proxy response

ドキュメントには、以下のように記載されています。
lambdaプロキシ統合をしている場合は、lambdaのレスポンスが所定の形式である必要があるとのことです。

https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format

![cap07.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/9ab8753a-7174-ca7f-472b-1930fd643762.png)

>In Lambda proxy integration, API Gateway requires the backend Lambda function to return output according to the following JSON format:

プロキシ統合では、API Gatewayはプロキシサーバー的な動作を行います。
具体的には、フロントからのリクエストはそのままlambdaへ、lambdaからのレスポンスはそのままフロントへプロキシします。
そのため、lambdaのレスポンスはWeb APIのレスポンス形式でなければならないわけです。

```output.json
{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
    "body": "..."
}
```

:x:　怒られるlambda

![cap04.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/1009614c-9e2d-e0ea-7bc2-90e982dee291.png)

:o:　怒られないlambda

![cap08.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/8f78f2fc-6ecb-d7c9-7581-09fe278454cb.png)


### おまけ：CORS設定
API Gatewayはプロキシサーバー的な動作を行うと上記しました。
フロントからのリクエストはそのままlambdaへ、lambdaからのレスポンスはそのままフロントへプロキシするイメージです。
この場合、CORS関係のレスポンスヘッダーもlambda側で設定する必要があります。

![cap10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/943e48cb-32ac-ee87-9a27-bd61f4f1b537.png)


## 2. **ライブラリによってPOSTの仕方が異なるので注意するべし！！**

これは読んで字のごとくです。気分でfetchとaxiosを使い分けてるのですが、POSTメソッドの仕様が異なります。

### fetch：

```fetch.ts
  const onClickFetch = async () => {
    try {
      const requestBody = {
        id: 123,
        name: 'api-gateway-test',
      }

      const response = await fetch(`${API_GATE_URL}/test`, {
        method: 'POST',
        body: JSON.stringify(requestBody),
      });

      console.log(response);
    } catch (error) {
      console.error('Error:', error);
    }
  }
```

第二引数のobjectのbodyはStringに変換しないといけません。

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch#%E6%9C%AC%E4%BD%93%E3%81%AE%E8%A8%AD%E5%AE%9A

### axios：

```axios.ts
  const onClickAxios = async () => {
    try {
      const requestBody = {
        id: 123,
        name: 'api-gateway-test',
      }

      const response = await axios.post(`${API_GATE_URL}/test`, requestBody,);

      console.log(response);
    } catch (error) {
      console.error('Error:', error);
    }
  }
```

第二引数にobjectをそのまま代入して大丈夫です。

https://github.com/axios/axios?tab=readme-ov-file#request-config

## 3. **フロントのリクエストボディはparseして利用！！！**


以下がドキュメントです。lambdaがAPI Gatewayから受け取るinput形式が記載されています。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format


ドキュメントにある通り、プロキシ統合では、handler関数の第一引数eventオブジェクト内にフロントエンドのリクエストボディが**stringで**代入されます。
具体的に言うと、event.bodyですね！

```input.json
{
  "resource": "/my/path",
  "path": "/my/path",
  "httpMethod": "GET",
  "headers": {
    "header1": "value1",
    "header2": "value1,value2"
  },
  ～（略）～
  "body": "Hello from Lambda!",
  "isBase64Encoded": false
}
```

lambda関数でリクエストボディにアクセスする場合、以下のようなコードになります。

```handler.js
exports.handler = async (event, context) => {
    const requestBody = JSON.parse(event.body)
    console.log(requestBody);

    /**
    * 処理 
    */

    return {
        statusCode : "200",
        headers: {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        },
    }
};
```

# おわりに

もう何回このミスするんだよ・・・なことをまとめました。
こういったふりかえりを何度もして、検索しなくても良い知識を増やしていきたいですね。