API Gateway 経由で SQS へメッセージを送る

## この記事について

- API Gateway から SQS へメッセージを送る方法について書いた記事です。
- API Gateway の AWS Proxy を使って、SQS の SendMessage API を呼び出します。 ※ MessageBody はクエリ文字列で指定します。
- AWS Proxy を使って連携を行うため、AWS Lambda の出番はありません。

## API Gateway の AWS Proxy って何?

API Gateway から Labmda 等を介さずに、直接 AWS API を呼び出しする機能です。
ドキュメントでは AWS 統合と表現されています。[^1]

この機能を使用して、API Gateway から SQS に直接メッセージを送ってみます。

## 構築手順

### 1. 事前準備

SQS キューの作成、API Gateway の API 作成を済ませておきます。
API Gateway から SQS の SendMessage API を実行するため、API Gateway の実行ロールを作成しておきます。

リファレンスを見た感じ、ミニマムにポリシーを作る場合、`sqs:SendMessage` を Allow するよう設定すれば良さそうですね。[^2]
サクッと試したい場合、AmazonSQSFullAccess というマネージドポリシーがあるので、それをアタッチしておきましょう。[^3]

### 2. リソース作成

テキトーにリソースを作成します

![011_create_resource.png](https://qiita-image-store.s3.amazonaws.com/0/79267/f230723f-2b59-5e26-ef2f-e291d04fc79b.png "アクション → リソースのアクション → リソースの作成")

テキトーにパラメーターを設定します。

|設定項目|内容|
|---|---|
|リソース名|Messages|
|リソースパス|/messages|

![012_submit_create_resource.png](https://qiita-image-store.s3.amazonaws.com/0/79267/2c1b86b9-c981-8d6b-1371-627896a8c62b.png "リソースの作成")

設定したら「リソースの作成」ボタンをクリック

### 3. メソッドを作成

リソースに対するメソッドを作成します。
使用する SQS の API である SendMessage は POST リクエストになるので、それをパクって POST リクエストを作成しましょう。

![013_create_method.png](https://qiita-image-store.s3.amazonaws.com/0/79267/5375b9a3-1d63-eae5-f65c-33cc5971b3d5.png "アクション → リソースのアクション → メソッドの作成")

メソッドを決めてチェックマークをクリック。

![014_create_post.png](https://qiita-image-store.s3.amazonaws.com/0/79267/50e3dcf9-ef69-e880-54a8-581b3fb3206a.png "POST メソッドの作成")

### 4. メソッドの設定

次は POST メソッドのセットアップ。
個人的に詰まったポイント。

![015_post_setup.png](https://qiita-image-store.s3.amazonaws.com/0/79267/a8f4ede1-7b6a-8b1c-1b89-bb540313888c.png "POST メソッドのセットアップ")

|設定項目|内容|備考|
|---|---|---|
|統合タイプ|AWS サービス|
|AWS リージョン|ap-northeast-1|使用する SQS キューがあるリージョンを指定|
|AWS サービス|Simple Queue Service (SQS)|API Gateway からリクエストを行う AWS サービスを選択|
|HTTP メソッド|POST|使用する API の Reference で確認<br>または、AWS CLI コマンドに --debug を付与して、出力を確認|
|アクションの種類|パス上書きの使用|
|パス上書き|000000000000/hello-sqs|AWS アカウント ID / SQS キュー名|
|実行ロール|arn:aws:iam::000000000000:role/APIGatewayExecuteRole|事前準備で作成した IAM Role の ARN|
|コンテンツの処理|パススルー|

SQS キューを作成すると ` https://sqs.[リージョン名].amazonaws.com/[AWS アカウント ID]/[SQS キュー名] ` といった形式で URL が生成されます。

その URL に API Gateway からリクエストを送信するため、上記設定が必要になります。

### 5. メソッドリクエスト、統合リクエストの設定

API Gateway から SQS キューに対してリクエストする設定を行いましたが、まだ具体的なパラメーターをどうするのか設定していません。
ここからパラメーターを設定していきます。

まずは、メソッドリクエストから。

![0211_method_request.png](https://qiita-image-store.s3.amazonaws.com/0/79267/030a426b-08b7-acca-1232-db290f2bf1eb.png "メソッドリクエスト")

「メソッドリクエスト」をクリック

![0212_method_request_detail.png](https://qiita-image-store.s3.amazonaws.com/0/79267/a24a4e68-b268-8296-68aa-190546f2c43b.png  "POST メソッドリクエスト")

|名前|必須|
|---|---|
|messagebody|✅|

※ 今回はリクエストの検証を設定していないので、「必須」の隣に警告マークみたいなのが表示されます。
リクエストの検証を設定する場合にはドキュメントを参考に。[^4]

次は統合リクエストを設定していきます。

![021_integration_request.png](https://qiita-image-store.s3.amazonaws.com/0/79267/3de5003b-35f2-9900-3647-a6b17949550c.png "統合リクエスト")

「統合リクエスト」をクリック

![022_integration_request_detail.png](https://qiita-image-store.s3.amazonaws.com/0/79267/31ef4412-c6ac-0717-4d8a-2576c412c4ca.png "統合リクエスト → URL クエリ文字列パラメータ")

URL クエリ文字列パラメーターを設定します

|名前|マッピング元|備考|
|---|---|---|
|Action|'SendMessage'|
|MessageBody|method.request.querystring.messageBody|

Action は SendMessage API を実行するのは固定なため、値は `'SendMessage'` となります。
MessageBody はクライアントからクエリ文字列で受け取った値を割り当てます。

SendMessage API に対して、どのようなパラメーター指定が必要となるかはリファレンスを確認しましょう。[^5]

### メソッドをテスト

設定したメソッドが期待通りに動作するか、マネジメントコンソール上でテストします。

![031_test.png](https://qiita-image-store.s3.amazonaws.com/0/79267/dc405193-8583-ea44-d82f-3edbe553bb8e.png "テスト")

「テスト」をクリック。 ※ ブラウザを小さくしすぎてデザインが崩れ気味・・・

![032_test_querystring.png](https://qiita-image-store.s3.amazonaws.com/0/79267/54dbdd12-ec23-b2a9-d03d-2099698cb8d1.png "テスト → クエリ文字列")

クエリ文字列だけ、下記のように指定します。

```
messageBody=sample-by-test
```

![033_test_submit.png](https://qiita-image-store.s3.amazonaws.com/0/79267/5e542977-a7d3-8509-5b00-5cde87707e8c.png "テスト")

「⚡テスト」をクリック

![034_test_result.png](https://qiita-image-store.s3.amazonaws.com/0/79267/7c7caed3-870f-0fb8-9e34-d2da36fd74b3.png "テスト実行結果")

ステータスで 200 がレスポンスされていれば OK!

下記のような出力が得られました。(長い)

レスポンス本文

```json
{
  "SendMessageResponse": {
    "ResponseMetadata": {
      "RequestId": "72c4d154-6bd4-518d-99d3-85cd0ea92a3f"
    },
    "SendMessageResult": {
      "MD5OfMessageAttributes": null,
      "MD5OfMessageBody": "266b344e1859386c47c43bf2502e9d87",
      "MessageId": "283178b3-0426-4501-aa98-f3fda899bb55",
      "SequenceNumber": null
    }
  }
}
```

レスポンスヘッダー

```json
{"X-Amzn-Trace-Id":"Root=1-5c1f633d-e97a52209ec49f5c90af34bd","Content-Type":"application/json"}
```

ログ

```txt
Execution log for request 7390005d-069d-11e9-82d4-530405135372
Sun Dec 23 10:28:13 UTC 2018 : Starting execution for request: 7390005d-069d-11e9-82d4-530405135372
Sun Dec 23 10:28:13 UTC 2018 : HTTP Method: POST, Resource Path: /messages
Sun Dec 23 10:28:13 UTC 2018 : Method request path: {}
Sun Dec 23 10:28:13 UTC 2018 : Method request query string: {messageBody=sample-by-test}
Sun Dec 23 10:28:13 UTC 2018 : Method request headers: {}
Sun Dec 23 10:28:13 UTC 2018 : Method request body before transformations: 
Sun Dec 23 10:28:13 UTC 2018 : Endpoint request URI: https://sqs.ap-northeast-1.amazonaws.com/000000000000/hello-sqs?Action=SendMessage&MessageBody=sample-by-test
Sun Dec 23 10:28:13 UTC 2018 : Endpoint request headers: {Authorization=*****************************************************************************************************************************************************************************************************************************************e59251, X-Amz-Date=20181223T102813Z, x-amzn-apigateway-api-id=nv5czfh87a, Accept=application/json, User-Agent=AmazonAPIGateway_nv5czfh87a, X-Amz-Security-Token=AgoGb3JpZ2luEN7//////////wEaDmFwLW5vcnRoZWFzdC0xIoACHDO4wYBxissppT8Wv4s0qSjvQPfpcZMDFwA6KJA46SkxaHCpIFriU6kJcNU9tv8nyOBp7IPcE/uEOYITaEgpuVDCtkKt/kz4FEmRI0NSTDYRjRMihNzP2GM7ABx8qqz0sX0dz6DfMYnzY3BgeBuTzStL+RxY7RJ69XGpbZFNYWmQCqJcTDYfpgKEfS5EaL435fsm+dK2W73H+T5qaGSqrrt9dXzMBw2cDGZg0Eq2EmWIYfc/zQelql8Y57AmixUZCTMnAafKjLWTfsIIoKjQGit1a5gJTHKkl/WBkdvqu+xcKxbZSdeh4en3uI2dghI6b3Kh9uAnzMm4iO9WTsMkiyqKAwik//////////8BEAAaDDk5ODUwODIwMDU1OSIMiL9KQWCmDkkJ+MrKKt4CDvIhbUiHaLFv/sv5mDzyGPZZ+qA6I3wcCwrYqgay4UZMQiMIIQxwSZ9nLmhiP14v6zVzMyncXNwPi9tezC5wgB/fdqwlEe3+tQ2Ny/bNGwKItqndnnAvspBI1tcQbDVRXLR8b5 [TRUNCATED]
Sun Dec 23 10:28:13 UTC 2018 : Endpoint request body after transformations: 
Sun Dec 23 10:28:13 UTC 2018 : Sending request to https://sqs.ap-northeast-1.amazonaws.com/000000000000/hello-sqs?Action=SendMessage&MessageBody=sample-by-test
Sun Dec 23 10:28:13 UTC 2018 : Received response. Integration latency: 32 ms
Sun Dec 23 10:28:13 UTC 2018 : Endpoint response body before transformations: {"SendMessageResponse":{"ResponseMetadata":{"RequestId":"72c4d154-6bd4-518d-99d3-85cd0ea92a3f"},"SendMessageResult":{"MD5OfMessageAttributes":null,"MD5OfMessageBody":"266b344e1859386c47c43bf2502e9d87","MessageId":"283178b3-0426-4501-aa98-f3fda899bb55","SequenceNumber":null}}}
Sun Dec 23 10:28:13 UTC 2018 : Endpoint response headers: {x-amzn-RequestId=72c4d154-6bd4-518d-99d3-85cd0ea92a3f, Date=Sun, 23 Dec 2018 10:28:13 GMT, Content-Type=application/json, Content-Length=276}
Sun Dec 23 10:28:13 UTC 2018 : Method response body after transformations: {"SendMessageResponse":{"ResponseMetadata":{"RequestId":"72c4d154-6bd4-518d-99d3-85cd0ea92a3f"},"SendMessageResult":{"MD5OfMessageAttributes":null,"MD5OfMessageBody":"266b344e1859386c47c43bf2502e9d87","MessageId":"283178b3-0426-4501-aa98-f3fda899bb55","SequenceNumber":null}}}
Sun Dec 23 10:28:13 UTC 2018 : Method response headers: {X-Amzn-Trace-Id=Root=1-5c1f633d-e97a52209ec49f5c90af34bd, Content-Type=application/json}
Sun Dec 23 10:28:13 UTC 2018 : Successfully completed execution
Sun Dec 23 10:28:13 UTC 2018 : Method completed with status: 200
```

動作確認ができたので、次はいよいよデプロイです。

ちなみに、上記ログが API Gateway の実行ログとなります。デプロイ後はステージ毎に設定が必要です。
クライアントから受け取ったリクエスト情報や、API Gateway での変換後の情報が記載されているので、デバッグやトラブルシュートで役に立ちます。[^6]

### API をデプロイ

「アクション」をクリックし、「API のデプロイ」をクリックします。

![041_deploy.png](https://qiita-image-store.s3.amazonaws.com/0/79267/9ebb1442-0c22-5f8d-c452-b961ad92fbe8.png "アクション → API アクション → API のデプロイ")

デプロイの設定を行います。

|設定項目|内容|備考|
|---|---|---|
|デプロイされるステージ|[新しいステージ]||
|ステージ名|hello|任意|
|ステージの説明||任意|
|デプロイメントの説明||任意|

![042_deploy_detail.png](https://qiita-image-store.s3.amazonaws.com/0/79267/f728976c-3bb9-a1ce-fd00-441fa024fe9b.png "API デプロイ設定")

入力したら「デプロイ」をクリック。

![043_deploy_result.png](https://qiita-image-store.s3.amazonaws.com/0/79267/daa6da45-dc11-0631-df6a-b3ea7c12a756.png "API デプロイ実行結果")

これで無事 API がデプロイされました。
「URL の呼び出し」に API へのアクセス URL が記載されています。

> URL の呼び出し: https://nv5czfh87a.execute-api.ap-northeast-1.amazonaws.com/hello

` https://[API Gateway API ID].execute-api.[リージョン名].amazonaws.com/[ステージ名] ` というフォーマットですね。

次は実際にデプロイされた API をテストしてみます。

### API をテスト

HTTP リクエストができれば良いので、シンプルに curl を使って API を呼び出してみます。

今までの設定状況はこんな感じ。

|設定項目|内容|
|---|---|
|アクセス先|https://nv5czfh87a.execute-api.ap-northeast-1.amazonaws.com/hello|
|パス|/message|
|クエリ文字列|messageBody=sample-by-curl|
|メソッド|POST|

実際にリクエストしてみましょう。

```bash
$ curl -X POST https://nv5czfh87a.execute-api.ap-northeast-1.amazonaws.com/hello/messages?messageBody=sample-by-curl
{"SendMessageResponse":{"ResponseMetadata":{"RequestId":"6abdd20b-6703-509d-8647-86920e8f1702"},"SendMessageResult":{"MD5OfMessageAttributes":null,"MD5OfMessageBody":"7b5d62b5e609a2e5767bf58aa408a4ac","MessageId":"1fe55fa7-fee0-4681-a5a8-6c136ade1732","SequenceNumber":null}}}
```

無事リクエストできているっぽいですね。

AWS CLI を使って、SQS キューからメッセージを取得してみましょう。

```bash
$ aws sqs receive-message --queue-url https://sqs.ap-northeast-1.amazonaws.com/000000000000/hello-sqs
{
    "Messages": [
        {
            "MessageId": "83f2d4d0-91f5-415d-97e2-d3f517718f48",
            "ReceiptHandle": "AQEBz8whWHFzsUx5lIJ/ICJDIH6IPmerbXNxhz9Pm4nx+D8Onm0KY/4jxNFKXsJWDeND8w2JKc6jUUMriMrumlIb88Nsqcqmjx9ARb66tMbq1b3F039V04f9SN0XwFXZvI9aCSWccgBE+LUgF6XZFSqjjwa7kPsz+5uZBFrR6fvMDcKFE/VSHvT5ylwMfgPVAkTB6iegUS721jOEqECNH7tIgNWZ93BxfG8lCaKoknHkOBp2xJftHZC4zlUwSJGmTpsHr7/sVB8//Y3uRVvuWNjjJsNsDrXVXIkqL+fEmNQ6THY6EB4Y+1UXYyg6rOizo08Ot+i5s4Kd/ya3GYm1Zsva+2QKutdmWtBPN1T75E32loBR2kks9NISMagjBuU3lA0L8J0uHQICLPiOGNHhrbmU1A==",
            "MD5OfBody": "7b5d62b5e609a2e5767bf58aa408a4ac",
            "Body": "sample-by-curl"
        }
    ]
}
```

マネジメントコンソールからテストした際のメッセージや、curl で指定した messageBody を持つメッセージを取得できるかと思います。

### クリーンアップ

作成した下記リソースを消しておきましょう。

- API Gateway の API
- API Gateway 実行ロールとして作成した IAM Role
- SQS キュー

## 今回作成した API の swagger

API Gateway では、作成した API をエクスポートできます。[^7]

今回作成した API を swagger yaml としてエクスポートした結果を記載しておきますので、構築が上手く行かなかった等あれば参考にどうぞ。

```yaml
---
swagger: "2.0"
info:
  version: "2018-12-23T10:40:52Z"
  title: "MyAPI"
host: "nv5czfh87a.execute-api.ap-northeast-1.amazonaws.com"
basePath: "/hello"
schemes:
- "https"
paths:
  /messages:
    post:
      produces:
      - "application/json"
      parameters:
      - name: "messageBody"
        in: "query"
        required: true
        type: "string"
      responses:
        200:
          description: "200 response"
          schema:
            $ref: "#/definitions/Empty"
      x-amazon-apigateway-integration:
        credentials: "arn:aws:iam::000000000000:role/APIGatewayExecuteRole"
        uri: "arn:aws:apigateway:ap-northeast-1:sqs:path/000000000000/hello-sqs"
        responses:
          default:
            statusCode: "200"
        requestParameters:
          integration.request.querystring.MessageBody: "method.request.querystring.messageBody"
          integration.request.querystring.Action: "'SendMessage'"
        passthroughBehavior: "when_no_match"
        httpMethod: "POST"
        type: "aws"
definitions:
  Empty:
    type: "object"
    title: "Empty Schema"
```

## 感想

Lambda や Fargate といった汎用性の高いサーバーレスサービスを使用する機会は多いかと思いますが、それらを使わずにマネージドサービスでサーバーレス環境を構築できると嬉しいですね。
また、API Gateway + SQS の組み合わせは、以下のようなメリットがあるかなーと思います。

- SPA 等のクライアントから API の呼び出し頻度をスロットリングによって抑制できる
- 呼び出し状況を API Gateway の CloudWatch メトリクスでモニタリングできる
    - 4XXError, 5XXError, Count, IntegrationLatency, Latency 等の有用なメトリクスが多くて嬉しい[^8]
- API Gateway の実行ログを有効化することでリクエストのロギングができる
- IAM や Cognito User Pool を使用して API 呼び出し権限を制御できる

ということで、割と実用的な組み合わせなんじゃないかなーと思いました。

API Gateway は名前から結構シンプルなサービスであるイメージを受けますが、様々な機能があり奥が深いサービスですね。

[^1]: AWS 統合で API Gateway API をビルドする https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/getting-started-aws-proxy.html

[^2]: Amazon SQS API のアクセス権限: アクションとリソースのリファレンス https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-api-permissions-reference.html

[^3]: Amazon SQS でのアイデンティティベース (IAM) のポリシーの使用 https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-using-identity-based-policies.html#sqs-access-policy-aws-managed-policies

[^4]: API Gateway でリクエストの検証を有効化する https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-method-request-validation.html

[^5]: SendMessage https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html

[^6]: API Gateway の API ログ作成をセットアップする https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-logging.html

[^7]: API Gateway から API をエクスポートする https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-export-api.html

[^8]: Amazon API Gateway のディメンションおよびメトリクス https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-metrics-and-dimensions.html