API Gatewayを利用して既存APIから返却されるJSONの構成を変えてクライアントに返却する

API GatewayのModelsとMapingテンプレートを利用することで既存APIから返却されるJSONの構成を変えてクライアントに返却できます。その内容について記載された[Walkthrough: API Gateway, Models, and Mapping Templates](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/getting-started-models.html)を試してみたのでメモ

# 参考

+ [Walkthrough: API Gateway, Models, and Mapping Templates](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/getting-started-models.html)

なお、Lambdaとの連携は以下にまとめました。

[API Gatewayを使ってアクセスキー認証でLambdaを実行する](http://qiita.com/toshihirock/items/8720118164a02dfdd11a)

# やること

以下のようなJSONを返却するAPIがあります。（実際に以下のURLで動きます)

```
$curl http://petstore-demo-endpoint.execute-api.com/petstore/pets
[
  {
    "id": 1,
    "type": "dog",
    "price": 249.99
  },
  {
    "id": 2,
    "type": "cat",
    "price": 124.99
  },
  {
    "id": 3,
    "type": "fish",
    "price": 0.99
  }
]%
```

上記をAPIをAPI Gatewayを経由して呼び出すことで以下のようにKeyを変更して返却されるようにします。(Ver.1)

```
[
  {
    "number": 1,
    "class": "dog",
    "salesPrice": 249.99
  },
  {
    "number": 2,
    "class": "cat",
    "salesPrice": 124.99
  },
  {
    "number": 3,
    "class": "fish",
    "salesPrice": 0.99
  }
]
```

また、以下のように構成を変えてみます(Ver.2)

```
[
  {
    "description": "Item 1 is a dog.",
    "askingPrice": 249.99
  },
  {
    "description": "Item 2 is a cat.",
    "askingPrice": 124.99
  },
  {
    "description": "Item 3 is a fish.",
    "askingPrice": 0.99
  }
]
```

最後の例ではそれぞれのアイテムを1行にまとめてみます(Ver.3)

```
{
  "listings": [
    "Item 1 is a dog. The asking price is 249.99.",
    "Item 2 is a cat. The asking price is 124.99.",
    "Item 3 is a fish. The asking price is 0.99."
  ]
}
```

# リージョンの設定

API Gatewayは2015年7月11日現在はtokyoリージョンで使えないため、リージョンはUS-EAST(N.Varginia)に設定します

# APIの作成

まずはAPIGatewayの画面を開いてAPIを作成します。

初めての場合、Get Startedを選択し、すでにAPIがある場合、create APIを選択します。選択すると以下のような画面となります。

![Screen Shot 2015-07-11 at 3.49.38 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/4fe52066-1e10-910e-484d-92980c068d87.png "Screen Shot 2015-07-11 at 3.49.38 PM.png")


作成するAPI名は **TestAPIGateway** としました。設定後、 **Create API** でAPIを作成します。

# [Ver.1]リソースの作成

次にリソースを作成します。リソースはアクセスする時のURLのパスの一部になります。　API作成後、以下のような画面になると思うので、 **Create Resource** を選択します。

![Screen Shot 2015-07-11 at 3.50.41 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/4cdd5ba6-8164-613c-c815-17f41fc13f0c.png "Screen Shot 2015-07-11 at 3.50.41 PM.png")

次の画面でResource名を決定します。　
本例では **changekey** としました。

![Screen Shot 2015-07-11 at 3.51.53 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/91819f00-0074-09fc-8e98-b8b7f3067061.png "Screen Shot 2015-07-11 at 3.51.53 PM.png")

指定後、 **Create Resource** でリソースを作成します。

# [Ver.1]メソッドの作成

次に上記リソースへのアクセスがあった際にどのような動作をするか決定するメソッドを設定します。

**Create Method**　ボタンを選択すると以下のように右側にどのHTTPメソッドを利用するかのセレクトとボックスが表示されます。

![Screen Shot 2015-07-11 at 3.56.28 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/b8839e1d-93a5-dce1-c150-6dbac48ceee2.png "Screen Shot 2015-07-11 at 3.56.28 PM.png")

今回の例ではGETメソッドを選択し、選択後にセレクトボックス右のチェック?ボタンを選択すると決定できます。

選択後、少し待つと登録が完了し、右ペインにメソッドのセットアップを行うための画面が表示されます。

![Screen Shot 2015-07-11 at 4.00.13 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/742f9b84-2aff-4c87-f500-9ab3d309f169.png "Screen Shot 2015-07-11 at 4.00.13 PM.png")

今回はAPI呼び出し時に **http://petstore-demo-endpoint.execute-api.com/petstore/pets** にリバースプロキシしたいので上記のように設定します。

+ Integration Type->HTTP Proxy
+ HTTP method->GET
+ Endpoint URL->http://petstore-demo-endpoint.execute-api.com/petstore/pets

設定が完了したら **Save** を選択します。

# [Ver.1]テストする

作成後、以下のような画面になり、APIが呼ばれた際にどのような流れでリクエストが流れるが確認できます。

![Screen Shot 2015-07-11 at 4.08.17 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/95fc581e-f1df-12bb-ffac-2e07c6960c94.png "Screen Shot 2015-07-11 at 4.08.17 PM.png")

右のペインのTEST(稲妻アイコン)を選択することでテストができます。

TEST(稲妻アイコン)を選択後、再度存在するTEST(稲妻アイコン)を選択することでリクエストが呼ばれた際のレスポンスを確認できます。

![Screen Shot 2015-07-11 at 4.10.35 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/364d9f75-83f1-99db-e926-6e1f1135833e.png "Screen Shot 2015-07-11 at 4.10.35 PM.png")

返却されるJSONの編集はしていないため、直接呼ばれた時と同じJSONが返却されることが確認できました。

# [Ver.1]モデルの作成

モデルではJSONを[JSON Scheme](http://json-schema.org/)によってデータの定義をします。APIGatewayによって返却される際のJSONの定義を行うモデルを作成します。

Amazon API Gatewayというロゴが表示された部分を選択します。
画面遷移後、作成したAPI一覧が確認できるので本例で作成したAPIの **2 Models** となっているリンクを選択します。

選択後、モデルを作成する画面になるので **Create** ボタンを選択します。

![Screen Shot 2015-07-11 at 5.02.37 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/d9c84c72-a4c5-5d64-8a15-1c66a890b1af.png "Screen Shot 2015-07-11 at 5.02.37 PM.png")


設定は上記画面のように以下のような設定としました。

+ Model name->PetsModelChangedKey
+ Content type->application/json
+ Model Schema->下部参照


```lang:ModelSchema
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "PetsModelChangedKey",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "number": { "type": "integer" },
      "class": { "type": "string" },
      "salesPrice": { "type": "number" }
    }
  }
}
```
最初の例ではKey名を変えたいのでid->number,type->class,price->salesPriceとそれぞれ変更するKey名のJSON定義となっています。構造は変わってないので変更されていません。

# [Ver.1]モデルとメソッドの紐付け

作成したモデルとメソッドの紐付けを行います。

再度、Amazon API Gatewayというロゴが表示された部分を選択します。
画面遷移後、本例で作成したAPIの **2 Resources** となっているリンクを選択します。

右側の **/changekey/GET** のリンクをクリックし、先ほどテストした時の画面を表示し、 **Method Response** のリンクを選択します。ここで返却するJSONの定義を記載します。

+ 200の横の矢印ボタンを選択
+ Response Models for 200の横の鉛筆アイコンを選択
+ セレクトボックスから先ほど作成したPetsModelChangedKeyモデルを選択
+ セレクトボックスの隣のチェックアイコン?を選択

![Screen Shot 2015-07-11 at 5.09.42 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/b63702a9-5968-211a-52d6-d80aadc913ff.png "Screen Shot 2015-07-11 at 5.09.42 PM.png")


次に **Integration Response** リンクを選択します。ここでは取得したJSONを返却するJSONにどのようにマッピングするか定義します。

+ 200の横の矢印ボタンを選択
+ 画面下部のTemplate Mappingsの位置にスクロールし、Content Type横の鉛筆アイコンを選択
+ Output passthroughのチェックを外す
+ Generate template from modelのセレクトボックスから PetsModelChangedKeyを選択
+ マッピングが表示される。変数を参照する形になっていないので以下のように少し変更
+  Content Type横のチェックアイコン?を選択

```
#set($inputRoot = $input.path('$'))
[
#foreach($elem in $inputRoot)
  {
    "number": $elem.id,
    "class": "$elem.type",
    "salesPrice": $elem.price 
  }#if($foreach.hasNext),#end

#end
]
```

![Screen Shot 2015-07-11 at 6.09.06 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20193/4c95fcde-f86e-c10c-509a-9e70d57a2fdd.png "Screen Shot 2015-07-11 at 6.09.06 PM.png")


# [Ver.1]結果を確認

準備ができたのでTEST(稲妻アイコン)を選択して返却されるJSONが変更されているか確認します。

成功していれば以下のようなレスポンスボディとなっていることが確認できます。

```
[
  {
    "number": 1,
    "class": "dog",
    "salesPrice": 249.99
  },
  {
    "number": 2,
    "class": "cat",
    "salesPrice": 124.99
  },
  {
    "number": 3,
    "class": "fish",
    "salesPrice": 0.99
  }
]
```

# [Ver.2]対応方法概要

同じ要領でやっていきます。

+ 任意の名称でリソースを作成
+ Getメソッドを作成
+ モデルを作成。以下のようにする。以下例の場合、モデル名は「PetsModelFlattenSome」とする

```
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "PetsModelFlattenSome",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "description": { "type": "string" },
      "askingPrice": { "type": "number" }    
    }
  }
}
```

+ モデルとメソッドを紐付ける
+ マッピングでは以下を指定

```
#set($inputRoot = $input.path('$'))
[
#foreach($elem in $inputRoot)
  {
    "description" : "Item $elem.id is a $elem.type.",
    "askingPrice" : $elem.price
  }#if($foreach.hasNext),#end

#end
]
```

テストを実行し、以下のようなレスポンスボディとなっていればOKです。

```
[
  {
    "description": "Item 1 is a dog.",
    "askingPrice": 249.99
  },
  {
    "description": "Item 2 is a cat.",
    "askingPrice": 124.99
  },
  {
    "description": "Item 3 is a fish.",
    "askingPrice": 0.99
  }
]
```

# [Ver.3]対応方法概要

同じ要領でやっていきます。

+ 任意の名称でリソースを作成
+ Getメソッドを作成
+ モデルを作成。以下のようにする。以下例の場合、モデル名は「PetsModelFlattenAll」とする

```
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "PetsModelFlattenAll",
  "type": "object",
  "properties": {
    "listings": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}
```

+ モデルとメソッドを紐付ける
+ マッピングでは以下を指定

```
#set($inputRoot = $input.path('$'))
{
  "listings" : [
#foreach($elem in $inputRoot)
    "Item number $elem.id is a $elem.type. The asking price is $elem.price."#if($foreach.hasNext),#end
        
#end
  ]
}
```

テストを実行し、以下のようなレスポンスボディとなっていればOKです。

```
{
  "listings": [
    "Item number 1 is a dog. The asking price is 249.99.",
    "Item number 2 is a cat. The asking price is 124.99.",
    "Item number 3 is a fish. The asking price is 0.99."
  ]
}
```

# デプロイする

まだデプロイをしていないのでインターネットから上記APIを叩くことができません。デプロイするためには **Deploy API**　ボタンを選択し、任意のステージにデプロイすればOKです。ステージの画面で実際にアクセスするURLも表示されます。

**ただし、この状態でデプロイすると特に認証なくアクセスできるのでIAMもしくはアクセスキーによる認証をした方が良いと思います** アクセスキーによる認証は以下に書きました。

[API Gatewayを使ってアクセスキー認証でLambdaを実行する](http://qiita.com/toshihirock/items/8720118164a02dfdd11a)

すでに作成されたメソッドで認証をアクセスキーにする場合、 テストができる画面で **Method Request** リンクを選択し、API Key RequiredをtrueにすればOKでした。リソースごとに認証有無及び認証方法も変えれるようです。

# 掃除する

テストなどができる画面などリソースを操作する画面の右上に **Delete API** ボタンがあるのでこれでAPIごと削除可能です。