SQS、SNS、SNS+SQSからのイベントを比較してみた

## はじめに
業務でLambdaと組み合わせてSQSやSNSをよく使うのですが、イベントを正しくハンドリングする必要があります。
イベントの構造についてドキュメント等があるかもしれませんが、検証した方が早いと思い、自分で検証環境を作って確認してみました。

## 概要

以下によるイベントの構造をLambdaでログ出力して検証します。

- SQS
- SNS
- SNS + SQS

## 設定

### Lambda

以下のLambdaを作成し、イベントをログ出力します。
```index.js
exports.handler = async (event) => {
    console.log(`event: ${JSON.stringify(event)}`); 
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};
```

### IAM
LambdaがSQSのメッセージを受け取れるようにするために、Lambda作成時に自動生成されたロールに対して、AmazonSQSFullAccessとAmazonSNSFullAccessを付与します。

### SQS

SQSを標準設定で作成し、先ほど作成したLambdaをトリガーに設定します。

### SNS

SNSを標準設定で作成し、以下をサブスクリプションとして指定します。
- 先ほど作成したSQS
- 先ほど作成したLambda

## 検証結果

### SQSの場合

SQSのコンソール画面からテストメッセージを送信してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/f83353f7-1d65-b6cf-d0dd-ecff6230076c.png)

#### イベント内容
`body`のところに先ほど送ったメッセージ`Test message`が入るようです。

```json
{
    "Records": [
        {
            "messageId": "xxx",
            "receiptHandle": "xxx",
            "body": "Test message",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1653699635640",
                "SenderId": "xxx",
                "ApproximateFirstReceiveTimestamp": "1653699635650"
            },
            "messageAttributes": {},
            "md5OfBody": "xxx",
            "eventSource": "aws:sqs",
            "eventSourceARN": "<arn_of_sqs>",
            "awsRegion": "ap-northeast-1"
        }
    ]
}
```

### SNSの場合

以下のメッセージをSNSから送信

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/f345749f-ff68-7675-64b4-75f891fa05c2.png)

#### イベント内容

Subject: メッセージのタイトル
Message: メッセージの本文
MessageAttributes: メッセージ属性

```json
{
    "Records": [
        {
            "EventSource": "aws:sns",
            "EventVersion": "1.0",
            "EventSubscriptionArn": "<arn_of_sns>",
            "Sns": {
                "Type": "Notification",
                "MessageId": "xxx",
                "TopicArn": "<arn_of_sns>",
                "Subject": "Test message from SNS - Title",
                "Message": "Test message from SNS - Raw message ",
                "Timestamp": "2022-05-28T01:13:53.904Z",
                "SignatureVersion": "1",
                "Signature": "xxx",
                "SigningCertUrl": "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-xxx.pem",
                "UnsubscribeUrl": "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=<EventSubscriptionArn>",
                "MessageAttributes": {
                    "key1": {
                        "Type": "String",
                        "Value": "\"Value1\""
                    },
                    "Key2": {
                        "Type": "String",
                        "Value": "[\"Value2\", \"Value3\"]"
                    }
                }
            }
        }
    ]
}
```

### SNS + SQSの場合

同様に、SNSから同じメッセージを送ります。

#### イベント内容

`body`の中にSNSのイベントが入るようです。SQSを経由するからっぽいですね。

```json
{
    "Records": [
        {
            "messageId": "xxx",
            "receiptHandle": "xxx",
            "body": "{\n  \"Type\" : \"Notification\",\n  \"MessageId\" : \"xxx\",\n  \"TopicArn\" : \"<arn_of_sqs>\",\n  \"Subject\" : \"Test message from SNS - Title\",\n  \"Message\" : \"Test message from SNS - Raw message\",\n  \"Timestamp\" : \"2022-05-28T01:26:45.690Z\",\n  \"SignatureVersion\" : \"1\",\n  \"Signature\" : \"xxx\",\n  \"SigningCertURL\" : \"https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-xxx.pem\",\n  \"UnsubscribeURL\" : \"https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=<arn_of_sqs>\",\n  \"MessageAttributes\" : {\n    \"key1\" : {\"Type\":\"String\",\"Value\":\"\\\"Value1\\\"\"},\n    \"key2\" : {\"Type\":\"String.Array\",\"Value\":\"[\\\"Value2\\\", \\\"Value3\\\"]\"}\n  }\n}",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1653701205731",
                "SenderId": "xxx",
                "ApproximateFirstReceiveTimestamp": "1653701205742"
            },
            "messageAttributes": {},
            "md5OfBody": "xxx",
            "eventSource": "aws:sqs",
            "eventSourceARN": "<arn_of_sqs>",
            "awsRegion": "ap-northeast-1"
        }
    ]
}
```

bodyの中を整形してみると、以下のようになり、SNSから直接Lambdaにメッセージを送った時と同様になりました。

```json
{
  "Type": "Notification",
  "MessageId": "xxx",
  "TopicArn": "<arn_of_sqs>",
  "Subject": "Test message from SNS - Title",
  "Message": "Test message from SNS - Raw message",
  "Timestamp": "2022-05-28T01:26:45.69Z",
  "SignatureVersion": "1",
  "Signature": "xxx",
  "SigningCertURL": "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-xxx.pem",
  "UnsubscribeURL": "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=<arn_of_sqs>",
  "MessageAttributes": {
    "key1": {
      "Type": "String",
      "Value": "\"Value1\""
    },
    "key2": {
      "Type": "String.Array",
      "Value": "[\"Value2\", \"Value3\"]"
    }
  }
}
```

## まとめ

結論、以下それぞれでイベントの構造が異なることが分かりました。
- SQS
- SNS
- SNS + SQS

これらを用いて開発する際はイベントの構造に注意するとよさそうです。