Amazon API Gateway

#Amazon API Gatewayとは#

- APIの作成：AWS Lambda、EC2、もしくはAWS外でパブリックとして公開されているアプリケーションをAPIとして公開することができます。
- APIの管理：Amazon API Gatewayは、APIの管理に必要な管理、運用などをお客様側での実施が必要なく、AWS側で提供してくれるマネジどサービスです。


#API GAtewayが扱うAPI#

##プロトコル##

|REST|WebSocket|
|---|---|
|ステートレス|ステートフル|
|単一HTTPメッセージで1つの操作に関する情報を(理想では)含む<br>扱う情報をURIで表現する「リソース」として定義し、それらをHTTPメソッド(PUT, GET, POST, DELETE, …)の表現で操作|HTTPの上で、クライアントとサーバー間の双方向通信を実現するための通信仕様<br>一つのコネクションで継続的なデータ送受信が可能<br>URIスキームは セキュアWebSocket用の「wss://~」を利用|

#REST#

##エンドポイント##

|エッジ最適化|リージョン|プライベート|
|---|---|---|
|一旦エッジロケーション(CloudFrontディストリビューション)にルーティング|リージョンに直接ルーティング|VPC内からAWS PrivateLink = VPCエンドポイント経由でのみアクセス可能|

##APIの作成##
APIは、1個以上のリソースを持ち、各リソースが1個以上のメソッドを持ちます。

API1---リソース1---メソッドGET
API2---リソース2---メソッドGET

各メソッドでは、アップストリームを選択し、
APIクライアント↔API Gateway間のやりとりと、
API Gateway↔アップストリームの間のやりとりを適切に行えるよう、
リクエストおよびレスポンスの変換設定を行います。

設定には、**メソッドリクエスト、メソッドレスポンス、統合リクエスト、統合レスポンス**の4つがあります。

|設定|説明|
|---|---|
|1.メソッドリクエスト|リクエストに認証が必要か、どのようなクエリパラメータを受け付けるかといったAPI Gatewayの受付設定を行う|
|2.統合リクエスト|アップストリームの指定、リクエストボディの変換といったAPI Gatewayとアップストリーム間の統合設定を行う|
|3.統合レスポンス|ステータスレコードのマッピング、レスポンス内容の変換といったアップストリームとAPIGateway間の統合設定を行う<br>【2.統合リクエスト】で「統合プロキシを統合」を指定した場合は設定不可|
|4.メソッドレスポンス|ステータスレコードごとのレスポンスヘッダーやレスポンスボディの設定といったAPI Gatewayからクライアントへのレスポンス設定を行う|


##APIの公開##
作成したAPIを外部に公開するためにはデプロイを行います。
各APIはデプロイ先としていくつかの**ステージ**を持ち、それぞれ異なるバージョンのAPIを公開することができます。
例えば、testステージを用意し動作に問題ないことが確認できたら、prodステージを用意するといったことが可能です。
カスタムドメイン名を設定することも可能です。
(例)
https://api.example.com/prod
https://api.example.com/staging
https://api.example.com/dev

#WebSocket#

##URL##

接続(wss)とWebSocket用(https)に2つのURLが提供される。

##ルート##

- 3つののルートが定義される。
 - $connect (接続時)
 - $disconnect (切断時)
 - $default (デフォルト)

##設定##

|設定|説明|
|---|---|
|1.ルートリクエスト|＊RESTと同じ|
|2.統合リクエスト|＊RESTと同じ|
|3.統合レスポンス|＊RESTと同じ|



#アクセス制御#

- **リソースポリシー(REST APIのみ)**
JSON形式のリソースポリシーを定義することで、API Gatewayの
どのリソースに、
どのアクセスからの、
どのようなアクションを許可、または拒否
するかを指定できます。

- **IAM認証**
APIの各要素へのアクセス権限を設定したIAMポリシーを作成し、IAMユーザやIAMロールに付与することで、APIへのアクセスの制御が可能になります。
これには、対象の各APIメソッドでIAM認証を有効化する必要があります。

- **Lambdaオーソライザー**
Lambda関数を作成することで、認証プロバイダーでの認証結果を元に、APIへのアクセス制御をメソッド単位で行えるようになります。
1.認証プロバイダーで認証を行い、トークンを取得する。
2.トークンを含めてAPIリクエストを行う。
3.API Gatewayがそのトークンの値を元にLambdaオーソライザーで認可を行う。
4.認可の結果を元にAPIGatewayがリクエスト許可または拒否をする。

- **Cognitoオーソライザー**
認証プロバイダとしてCognitoユーザープールを用いて、APIへのアクセス制御をメソッド単位で行うことも可能です。仕組みはLambdaオーソライザーと似ていますが、この場合はLambda関数を作成する必要はありません。
1.Cognitoユーザープールで認証を行い、トークンを発行する。
2.トークンを含めてAPIリクエストを行う。
3.APIGatewayがそのトークンの値を元にCognitoユーザープールで認可を行う。
4.認可の結果を元にAPIGatewayがリクエスト許可または拒否をする。



#統合リクエスト#

|統合リクエストタイプ|説明|
|---|---|
|Lambda |Lambda関数を指定して呼び出す。クロスリージョン、クロスアカウントでの呼び出しに対応。|
|HTTP|インターネット経由で呼び出し可能なURL(HTTPS)とメソッドを指定して呼び出す。パブリックに到達可能なエンドポイントであれば実装場所は問わない|
|Mock|モックとしてAPI Gatewayで直接、固定的な応答を返す。統合レスポンスのマッピングテンプレートで実際のモック応答データを設定|
|AWSサービス|AWSサービスを直接呼び出して、各サービスで用意されているアクションを実行|
|VPCリンク|NLBを参照する「VPCリンク」を登録しそれを経由してVPC内リソースへアクセス。エンドポイントURLとしてNLBホスト名を指定|

詳しくは、[AWS Black Belt](https://d1.awsstatic.com/webinars/jp/pdf/services/20190514_AWS-Blackbelt_APIGateway.pdf)を確認することをおすすめする。

なお、プロキシ統合についても確認しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/e4ae382f-d434-bc0e-603e-5355c038b720.png)



#APIの管理#

##スロットリングと使用量プラン##
アップストリームで処理するリクエスト数が大きくなりすぎないように、リクエスト数を制限することができる。
AWSアカウント単位、メソッド単位、APIキー単位で制御することができます。
**レート**（1秒あたりのリクエスト数）と**バースト**(リクエストの同時実行数)で設定します。

**APIキーとは**
API Gatewayの使用量を制限するために発行するアクセスキーです。
APIキーの使用量制限はそれぞれのAPIキーに対して設定するのではなく、**使用量プラン**という設定項目でスロットリング設定を行い、APIキーを使用量プランに紐付けることで設定します。
レートやバーストの他に、**クォータ**(日、週、月あたりのリクエスト数)も設定します。


##API WAFでの攻撃からの保護##

ステージごとに設定することで、SQLインジェクションやクロスサイトスクリプティングといったWebアプリケーションに対する攻撃から保護することができます。


##カスタムドメイン##

「カスタムドメイン」を登録し、証明書とDNSを設定することで独自ドメイン名で登録したAPIを呼び出し可能 (※エンドポイントタイプが「プライベート」以外)

(例)
エンドポイントURL https://<api-id>.execute-api.<region-id>.amazonaws.com/<stage-name>)
⬇️
カスタムドメイン　https://api.example.com/my-api-id-service

##キャッシュ##

REST APIでは APIのステージ毎にキャッシュを定義し、バックエンドへのトラフィック削減と低レイテンシの実現に利用可能

##カナリアリリース##

REST APIの各ステージ(メイン)に紐付く特別なステージ「Canary」を作成し、リクエストを指定の比率でCanaryへ流すことが可能

##X-Ray連携##

REST APIでは APIステージのログ設定としてAWS X-Rayへの連携によるリクエストのトレースと分析およびデバッグが可能

##CloudWatchメトリクス##

設定は、「ステージ」から行うことができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/6f6ac236-a2a8-b472-6332-894c5e99cebf.png)

|詳細メトリクス|説明 |
|---|---|
|4XXError|指定された期間に取得されたクライアント側エラーの数。|
|5XXError|指定された期間に取得されたサーバー側エラーの数。|
|CacheHitCount|指定された期間内に API キャッシュから配信されたリクエストの数。|
|CacheMissCoun|API キャッシュが有効になっている特定の期間における、バックエンドから提供されたリクエストの数。|
|Count|指定された期間内の API リクエストの合計数。|
|IntegrationLatency|API Gateway がバックエンドにリクエストを中継してから、バックエンドからレスポンスを受け取るまでの時間。|
|Latency|API Gateway がクライアントからリクエストを受け取ってから、クライアントにレスポンスを返すまでの時間。|

[API Gateway API をトラブルシューティングするために CloudWatch ログを有効化するにはどうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/api-gateway-cloudwatch-logs/)



#APIドキュメント#
利用者が活用しやすく、**ドキュメント**を公開すべきです。
API Gatewayには、APIに関するドキュメントを作成する機能が用意されています。
この機能を利用すれば、作成したAPIのリソース、メソッド、リクエスト、レスポンスなどの各要素に説明をつけるだけでなく、**Swagger(OpenAPI)**と呼ばれるAPIドキュメントの標準的な形式に測ったAPIドキュメント定義ファイルを、エクスポートすることができます。エクスポートされたAPIドキュメント定義ファイルは、標準的な形式なので、SwaggerUIなどのツールを利用することで、開発者にとって見やすくデザインされたフォーマットを閲覧できます。
また、これとは逆に、Swagger形式のAPIドキュメント定義ファイルをAPI Gatewayにインポートし、APIの作成を開始することもできます。



#利用料金の考え方#

![スクリーンショット 2019-11-02 21.20.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/fccc6c14-c39a-74bc-8be3-05f0ab18435d.png)


#エラー処理一覧#

[Handling Errors in Amazon API Gateway](https://docs.aws.amazon.com/apigateway/api-reference/handling-errors/)


#ゲートウェイレスポンスのタイプ#

##INTEGRATION_FAILURE(504)##
統合がタイムアウトした場合のゲートウェイレスポンス。レスポンスタイプが未指定の場合、このレスポンスはデフォルトで DEFAULT_5XX タイプになります。
統合タイムアウトの範囲は、Lambda、Lambdaプロキシ、HTTP、HTTPプロキシ、AWS統合を含むすべての統合タイプの50ミリ秒から29秒です。 


#Demo(Lambda プロキシ統合による Hello World API の構築)#

なお、プロキシ統合の説明については↓の記事がわかりやすいです。
[AWS Lambda Proxy Integrationを試してみた](https://qiita.com/_mogaming/items/2bd83204e212e35b2c6c)

##API のバックエンドとなる「Hello, World!」Lambda 関数を作成する##

次の関数は、JSON形式で発信者に挨拶を返します。

```
{
    "greeting": "Good {time}, {name} of {city}.[ Happy {day}!]"
}
```

関数は、Node.js 8.10で以下のとおり作成する。

```Node.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;
};
```


##「Hello, World!」 API を Lambda プロキシ統合を使用して作成する##

「APIの作成」で書いたように、API　→　リソース　→　メソッドの順に作成していきます。

###API###
「RESET」を選択し、「新しいAPI」を作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/18a9c034-6493-318f-8750-abdbec063809.png)

###リソース###
ここでは、「リソース名」だけを入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/c9fc31ee-a65a-fd41-400c-499a496b34dc.png)

###メソッド###
ANYメソッドを選択し、先程作成したLambda関数を選択する。
![スクリーンショット 2019-08-03 17.07.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/bec3ce06-7d93-6693-2068-08311c710caa.png)

次に「APIの公開」で書いたようにデプロイして行きます。

###デプロイ###
「アクション」-「APIのデプロイ」で適当な名前をつけてデプロイするとURLを作成される。
![スクリーンショット 2019-08-03 17.17.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/43a8e1d1-37be-c206-daab-4e3cc70c1528.png)

##テストする##

これでデプロイされたので、アクセスして確認して見ましょう。

`https://**********.execute-api.ap-northeast-1.amazonaws.com/test/helloworld?name=naata&city=Japan`



#リンク#
[API Gateway - API キャッシュを有効にして応答性を強化する](https://qiita.com/leomaro7/items/c13c5cee168ed56ccc38)
[ステージごとにAPIを作成することなく 各ステージごとにバックエンドポイントを振りわける](https://qiita.com/leomaro7/items/86ae586d278265720429)
[API Gatewayのカスタムドメイン,HTTPS,相互TLS認証](https://qiita.com/leomaro7/items/6a364bf8edfdc05c2231)