AWS API Gateway プロキシ統合・カスタム統合のチュートリアルをやってみた

# 初めに

以下のチュートリアルをやってみました。これまで API Gateway の基本的な部分が分からなかったのですが、カスタム統合のチュートリアルに着手することで理解できました。マッピングテンプレートやクエリ文字列パラメータ、リソース・メソッド、API のデプロイについて理解が深まりました。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/getting-started-lambda-non-proxy-integration.html

# Lambda プロキシ統合とは

API Gateway の API 統合の種類の一つです。バックエンドが Lambda であり、 統合リクエスト・統合レスポンスの設定が不要な統合タイプです。

| 統合エンドポイントのタイプ | 使用可能なAPI統合タイプ               |
| :------------------------- | :------------------------------------ |
| Lambda関数                 | Lambdaプロキシ統合 Lambdaカスタム統合 |
| HTTPエンドポイント         | HTTPプロキシ統合 HTTPカスタム統合     |
| AWSサービスアクション      | 非プロキシタイプの AWS 統合のみ       |

![0.1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/496a5e1f-9441-cdc1-7a8e-f9dbc1939745.png)

## Lambda 関数を作成する

ナビゲーションペインから「関数」を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8fbdf60c-ea70-19a9-42d1-8da340c15e12.png)

「関数の作成」をクリックします。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/ed6c5655-f29a-74be-bff8-aedf8f614466.png)

関数の名前、ランタイム、ロールを設定します。ポリシーテンプレートは空白にしておきます。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/2fa363e8-345d-372c-3789-83324a0ddc93.png)

関数のコードに以下をペーストします。

```js
'use strict';
console.log('Loading hello world function');
 
exports.handler = async (event) => {
    let name = "you";
    let city = 'World';
    let time = 'day';
    let day = '';
    let responseCode = 200;
    console.log("request: " + JSON.stringify(event));
    
    if (event.queryStringParameters && event.queryStringParameters.name) {
        console.log("Received name: " + event.queryStringParameters.name);
        name = event.queryStringParameters.name;
    }
    
    if (event.queryStringParameters && event.queryStringParameters.city) {
        console.log("Received city: " + event.queryStringParameters.city);
        city = event.queryStringParameters.city;
    }
    
    if (event.headers && event.headers['day']) {
        console.log("Received day: " + event.headers.day);
        day = event.headers.day;
    }
    
    if (event.body) {
        let body = JSON.parse(event.body)
        if (body.time) 
            time = body.time;
    }
 
    let greeting = `Good ${time}, ${name} of ${city}.`;
    if (day) greeting += ` Happy ${day}!`;

    let responseBody = {
        message: greeting,
        input: event
    };
    
    // The output from a Lambda proxy integration must be 
    // in the following JSON object. The 'headers' property 
    // is for custom response headers in addition to standard 
    // ones. The 'body' property  must be a JSON string. For 
    // base64-encoded payload, you must also set the 'isBase64Encoded'
    // property to 'true'.
    let response = {
        statusCode: responseCode,
        headers: {
            "x-custom-header" : "my custom header value"
        },
        body: JSON.stringify(responseBody)
    };
    console.log("response: " + JSON.stringify(response))
    return response;
};
```

「Deploy」をクリックします。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/3ddec41c-0912-84a8-ed4b-7fc2745ec364.png)

「Changes deployed」が表示されたことを確認します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f6d91b92-d5f2-ab9b-6d76-53c3d3559dc2.png)

## REST API を作成する

「APIを作成」をクリックします。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/d69c6006-41ff-d9bd-5820-1571f4a69196.png)

REST API の「構築」をクリックします。

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/1ab60d48-5660-9075-bd50-a1e1d7d6ec85.png)

以下のようにプロトコルを設定します。

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/bb431bd7-d495-2cab-4cbd-1bb3cd52046b.png)

「アクション」のドロップダウンから「リソースの作成」をクリックします。

![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/53a69064-bdf3-0391-d307-4ed00b2d892f.png)

リソース名を入力し、「リソースの作成」をクリックします。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/7cfbb430-e57e-087c-4d2e-d85461370bb9.png)

作成したリソースを選択した状態で「メソッドの作成」をクリックします。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8db5696d-fbb9-b6bd-fd7e-591d09ed7a08.png)

「ANY」を選択し、横のチェックマークをクリックします。

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/4e04be0d-02b6-9dfa-8eec-fbe59a282350.png)

以下のように設定します。

![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/b2cf1f01-ffb0-e0c4-ceff-48603663dc2e.png)

権限を追加するかどうか聞かれるので「OK」をクリックします。

![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/0622cf13-8cf9-8fac-1e5a-98c74f716fc4.png)

「API のデプロイ」をクリックします。

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f813f629-ade5-1d07-2e80-5e15d888c768.png)

以下の URL はリクエストに使用します。

![16.1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/197545bf-ce0e-b3cb-d182-d22ccc1259f3.png)

**リクエスト**

```
PS C:\tutorial> curl.exe -v -X POST "https://API_Gateway_ID.execute-api.ap-northeast-1.amazonaws.com/test/helloworld?name=John&city=Seattle" -H "content-type: application/json" -H "day: Thursday" -d '{ "time": "evening" }'
```

**Lambda が受け取ったイベント**

API Gateway は上記のリクエストを Lambda に渡すために次のような JSON フォーマットに変換します。

```json
{
    "resource": "/helloworld",
    "path": "/helloworld",
    "httpMethod": "GET",
    "headers": {
        "Accept": "*/*",
        "content-type": "application/json",
        "day": "Thursday",
        "Host": "API_Gateway_ID.execute-api.ap-northeast-1.amazonaws.com",
        "User-Agent": "curl/7.55.1",
        "X-Amzn-Trace-Id": "Root=1-60fbfb9a-70429bfb1ef089820112571c",
        "X-Forwarded-For": "11.22.333.44",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "Accept": [
            "*/*"
        ],
        "content-type": [
            "application/json"
        ],
        "day": [
            "Thursday"
        ],
        "Host": [
            "API_Gateway_ID.execute-api.ap-northeast-1.amazonaws.com"
        ],
        "User-Agent": [
            "curl/7.55.1"
        ],
        "X-Amzn-Trace-Id": [
            "Root=1-60fbfb9a-70429bfb1ef089820112571c"
        ],
        "X-Forwarded-For": [
            "11.22.333.44"
        ],
        "X-Forwarded-Port": [
            "443"
        ],
        "X-Forwarded-Proto": [
            "https"
        ]
    },
    "queryStringParameters": {
        "city": "Seattle",
        "name": "John"
    },
    "multiValueQueryStringParameters": {
        "city": [
            "Seattle"
        ],
        "name": [
            "John"
        ]
    },
    "pathParameters": null,
    "stageVariables": null,
    "requestContext": {
        "resourceId": "il7kor",
        "resourcePath": "/helloworld",
        "httpMethod": "GET",
        "extendedRequestId": "C-RANGv6tjMFtiA=",
        "requestTime": "24/Jul/2021:11:38:02 +0000",
        "path": "/test/helloworld",
        "accountId": "123456789012",
        "protocol": "HTTP/1.1",
        "stage": "test",
        "domainPrefix": "API_Gateway_ID",
        "requestTimeEpoch": 1627126682797,
        "requestId": "7a6335b9-554b-43a7-a228-3a9fb9f10a73",
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "sourceIp": "11.22.333.44",
            "principalOrgId": null,
            "accessKey": null,
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "curl/7.55.1",
            "user": null
        },
        "domainName": "API_Gateway_ID.execute-api.ap-northeast-1.amazonaws.com",
        "apiId": "API_Gateway_ID"
    },
    "body": null,
    "isBase64Encoded": false
}
```

**レスポンス**

```json
{
    "message":"Good day,John of Seattle. Happy Thursday!",
        "input":{
            "resource":"/helloworld",
            "path":"/helloworld",
```

「CloudWatch のログを表示」をクリックすれば、Lambda 関数のログを確認できます。

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9c32d9bc-8640-e4e5-639e-d4671d1c8134.png)

# Lambda 非プロキシ統合（カスタム統合）とは

メソッドリクエストをどのように統合リクエストにマッピングするか、統合レスポンスをどのようにメソッドレスポンスにマッピングするかを設定する Lambda 統合です。

![0.2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/cb77fb09-b3cd-df01-08f1-522bb812d25f.png)

## Lambda 関数のコードを置き換える

以下のコードで最初に作成したコードを置き換えます。

```js
'use strict';
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];            
var times = ['morning', 'afternoon', 'evening', 'night', 'day'];

console.log('Loading function');

exports.handler = function(event, context, callback) {
  // Parse the input for the name, city, time and day property values
  let name = event.name === undefined ? 'you' : event.name;
  let city = event.city === undefined ? 'World' : event.city;
  let time = times.indexOf(event.time)<0 ? 'day' : event.time;
  let day = days.indexOf(event.day)<0 ? null : event.day;

  // Generate a greeting
  let greeting = 'Good ' + time + ', ' + name + ' of ' + city + '. ';
  if (day) greeting += 'Happy ' + day + '!';
  
  // Log the greeting to CloudWatch
  console.log('Hello: ', greeting);
  
  // Return a greeting to the caller
  callback(null, {
      "greeting": greeting
  }); 
};
```

## REST API を作成する

`/` の下に `city` というリソースを作成します。リソースパスは**波括弧を付けて `{city}`** とします。「API Gateway CORS を有効にする」にチェックを入れます。

![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/3303e7a2-19a1-f787-4b0e-564e1f2cfbdd.png)

メソッドは「ANY」を選択し、Lambda 関数を指定します。プロキシ統合にチェックを入れずにを「保存」をクリックします。

![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f9893f5f-cb63-1e25-b89f-bf9338f2e4f9.png)

メソッドリクエストを編集します。

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/cf6e60e2-b04e-9393-7dc0-e6fd5aabbb0e.png)

「クエリ文字の追加」をクリックします。

![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/932a558e-44e2-03bc-5c0f-83e378623e74.png)

以下のように設定します。

![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/fe86487c-2dde-433e-a625-975789e37390.png)

ヘッダーを以下のように追加します。

![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9ffc1634-808a-71e6-ca21-9e0bf33c828b.png)

「モデル」を選択します。

![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/880241b5-7a1d-2076-99df-3f194917e85c.png)

「作成」をクリックします。

![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/e26c9240-06a9-1ee7-ae80-db40e6b747ef.png)

モデル名を入力し、コンテントタイプに「application/json」と入力します。モデルのスキーマは以下を貼り付けます。

```json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "GetStartedLambdaIntegrationInputModel",
  "type": "object",
  "properties": {
    "callerName": { "type": "string" }
  }
}
```

![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/5f3330e0-6a01-93dc-c112-08fc10739e3f.png)

リクエスト本文に作成したモデルを指定します。

![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/cc494cfc-75e4-b981-b2ec-f9c2ce3c839f.png)

統合リクエストを編集します。

![28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/56d91042-67f8-b14b-463a-5387f4e3585e.png)

「マッピングテンプレートの追加」をクリックします。「application/json」と入力し、チェックマークをクリックします。

![29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/a8720251-1815-3b6b-7751-4f126fcd8d32.png)

「はい、この統合を保護します」をクリックします。

![29.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/aa581023-840a-7677-aafb-58742c6aadfb.png)

以下のように設定されていることを確認します。

![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8373a580-1a72-5d47-5af5-75a6ddcca8e1.png)

テンプレートの生成に作成したモデルを選択します。本文は以下で置き換えます。

```json
#set($inputRoot = $input.path('$'))
{
  "city": "$input.params('city')",
  "time": "$input.params('time')",
  "day":  "$input.params('day')",
  "name": "$inputRoot.callerName"
}
```

![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/0e69a2b0-180c-5cc5-82c9-9ab6caaf081c.png)

テストの稲妻マークをクリックします。

![32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/81859214-73fc-7b33-c509-e308d0eee73e.png)

メソッドは「POST」、パスは「Seattle」、クエリ文字列は「time=morning」、ヘッダーは「day:Wednesday」、リクエスト本文は「{ "callerName":"John" }」を入力します。

![33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/aafc3576-827c-08e9-8f5c-722239fe800f.png)

ステータスが 200 となっていれば成功です。

![34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/458ab178-3aa4-c404-d800-1f32fe912e05.png)

API のデプロイをします。ステージ変数は「custom」と入力します。

![34.1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/4825b4ce-fc78-b30c-7f2d-73574bab58ed.png)

以下の URL にリクエストを送ります。

![35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/9cf49f09-7522-15a4-c5c7-2804dd6b9fca.png)

**リクエスト**

```
PS C:\tutorial> curl.exe -v -X POST "https://API_Gateway_ID.execute-api.ap-northeast-1.amazonaws.com/custom/Seattle?time=evening" -H "content-type: application/json" -H "day: Thursday" -H "x-amz-docs-region: ap-northeast-1" -d '{"callerName": "John"}'
```

- `/Seattle` ・・・ リソース。これが `city: 'Seattle'` にマッピングされる。
- `?time=evening` ・・・ クエリ文字列パラメータ。これが `time: 'evening'` にマッピングされる。
- `day: Thursday` ・・・ HTTP リクエストヘッダー。これが `day: 'Thursday'` にマッピングされる。
- `{"callerName": "John"}` ・・・ リクエスト本文。これが `name: 'John'` にマッピングされる。

上記のパラメータは以下の設定に対応しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8b77d130-e9c7-b90a-d2e7-fc05771972f1.png)

**Lambda が受け取ったイベント**

```
{ city: 'Seattle', time: 'evening', day: 'Thursday', name: 'John' }
```

上記のイベントは統合リクエストのマッピングテンプレートによって作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/ddcd5bf6-dd3e-1cb7-bc19-550245f57783.png)


**レスポンス**

```json
{"greeting":"Good evening, John of Seattle. Happy Thursday!"}
```

# 参考記事

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/getting-started-lambda-non-proxy-integration.html

https://devadjust.exblog.jp/22690878/

https://online.dhw.co.jp/kuritama/query-string/