モダンな技術のAWS AmplifyとSESでGraphQL APIを叩きLambda関数でメール送信機能実装！！

# まずAWS Amplifyとは？

簡単に言うと環境構築をクラウド側に任せて自分達は開発に集中することが可能にしてくれるサービス

**バックエンドをAPI化してフロントエンドで処理する**
バックとフロントを一体化する事を可能にしてくれます。

デプロイも簡単に行ってくれてCD(継続的デリバリー)もGitHubにpushするだけでデプロイできます。

まとめると、AWS(amplify)とローカル環境(フロントエンド)とGitHubさえあればアプリ開発ができる！！サーバーレスなので自分でのサーバーを管理・運用する必要性がなくなり、AWSにお任せできる！コストもいうほど高くない！
スタートアップ企業さんにとっては最高の条件だと思います！

すごく便利なサービスですが一つ弱点があるとしたら`Amplify`はSPAしか対応していないところです。

# AWS SESとは？

### 大量の電子メールを配信するためのクラウドベースのサービス。

自作のアプリから顧客にメールを送ることができコストも低価格でAWSなのでセキュアな環境で安全に実装できます。

全体の流れとしては、Eメールを送信したらSESが受け取りSES経由で受信者に送ると言う流れです

基本的にはAmplifyを使うならLambda関数に処理を書くことになると思います！

本番環境に反映するときはサンドボックス制限を解除した方が良さそうです。

## 運用していく中で意識していくもの(大事なこと)

AWSの公式ドキュメントでは、
「基本的に**バウンス率は 5% 以下**に維持してください。」
「基本的に**苦情率は 0.1% 未満**に維持してください。」
とのことです、これは運用していく中での目安です
バウンスとは一言で: **届かなかったメールの返送**
苦情率とは一言で:**迷惑メールとして報告されたメールの割合**

## これから手順をざっとまとめました！
まずは、ローカルでLambda関数の作り方です。

-  `amplify add funciton`でLambda関数を作成。amplify/backend/function/関数名/src
こちらのディレクトリに`index.js`が作成されますのでこれがLambdaを書くファイルとなります。勿論、TypeScriptに変換も可能です。

コードが書き終わったら、TypeScriptの場合はデプロイする前に`tcs ファイル名.ts`でビルドすること！

::: note warn
注意点:`package.json`が作成されますがこれは、プロジェクト用のとLambda用で分かれますので2つあって正解です。
:::

ビルドがしっかりできたら、distディレクトリにビルドされた.jsファイルがあるか確認。

### amplify pushでバックエンド環境にデプロイ
- `amplify status`で現在のバックエンド環境を確認してから、`amplify push`してAWSのLambdaコンソールの関数一覧に反映されているか確認

### ZIPファイルの容量に注意！
- 容量が大きいとソースコードが表示されない時があるのでdistディレクトリにある
`latest-build.zip`ファイルの中身を確認して使用しないファイルを削除
基本的にnode_modulesの使わないものを削除、aws-sdkはlambdaで用意してくれているため削除していい。
ローカルでは作るけど本番環境(amplify push)では作らない感じです。
※**容量が多い**とうまくファイルがデプロイができなくてビルドされた.jsファイルがありませんと怒られます(自分は何度もそうなりました)
### AppSyncにAPIを作成する
`amplify add api`でAppSyncにエンドポイントを作成する。ここでRESTかGraphQLどちらかを選択できます。

amplify/backend/api/アプリ名/schema.graphqlが作られる→必要なschemaを書く。
`amplify push`するとバックエンド環境に情報が反映されデプロイされる。

::: note
豆知識
もしAPIをテストしたい、完全じゃないから開発環境で確認したい！ってときは`amplify mock api`で一時的に作成してくれます。
**このとき大量のresolverが生成されますがmock解除したらしっかりと消えますのでご安心を。**
:::

### appsyncのデータソースの設定
AppSyncコンソールに行き、APIsを選択し特定のバックエンド環境を選び、左端にあるデータソースを選択して、データソースタイプはAWSLambda関数で、作成したLambda関数のARNを選択し必要な権限のあるロールを選択してから保存する

![スクリーンショット 2023-08-31 22.29.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124781/f580f117-2016-27af-884f-c71de2f8fcc6.png)



### 特定のAPIを叩いた時にLambda関数を呼び出すための方法

トリガーが特定のGrahpQL APIを叩いた時にLambda関数を発火するようにため、VTLという言語を使いカスタムリゾルバにLambda関数を呼び出す処理を書きます。

::: note

**どうやって作るの？**
AppSyncに戻っていたただき、関数を選択してAWSのコンソールで関数を作成するか、ローカルで作成して`amplify push`するかのどっちかです。
ローカルの場合は、`プロジェクトのrootpath/amplify/backend/api/アプリ名/resolvers/Mutation.createテーブル名.req.vtl`
今回はメールを送信した際に発火してほしいためcreateです。
:::

そして、pushしたらAppSyncかローカルで作成したリゾルバ関数(リクエストマッピングテンプレート)の詳細に行き、データソースを選択で先ほど作ったそのLambda関数に許可された権限が入ったデータソースを選択する(ここ大事)
これを忘れると権限エラーが出ます。
### データソースを作成して呼び出すリゾルバに権限を設定する流れ
1. AppSyncからデータソースタイプはLambda関数でのLambdaのARNを指定して管理者からいただいた許可された権限のロールを選択
1. 次に関数を選んで先ほど作ったデータソースを選択する

### 権限について
権限に関しては、管理者から解除を受けています。
また、`amplify add function`で作成されるファイルの中にポリシーを記述する必要があります。具体的には、`amplify/backend/function/関数名/関数名-clocdformation-template.json`に以下のSESとDynamoDBのポリシーを追加します
```json
{
  "Effect": "Allow",
  "Action": [
    "ses:SendEmail",
    "ses:SendRawEmail"
  ],
  "Resource": "*"
}
{
  "Effect": "Allow",
  "Action": [
    "dynamodb:PutItem"
  ],
  "Resource": {
    "Fn::Sub": [
      "arn:aws:dynamodb:${region}:${account}:table/${tableName}",
      {
        "region": {
          "Ref": "AWS::Region"
        },
        "account": {
          "Ref": "AWS::AccountId"
        },
        "tableName": "ここに関係するテーブル名を記述"
      }
    ]
  }
}
```

次に、Lambdaのポリシー設定の後、リゾルバ関数のポリシー設定が必要です。
具体的には、amplify/backend/アプリ名/stacks/CustomResources.jsonに以下の内容を追加します
```json
"Resources": {
    "EmptyResource": {
      "Type": "Custom::EmptyResource",
      "Condition": "AlwaysFalse"
    },
    "LambdaDataSource": {
      "Type": "AWS::AppSync::DataSource",
      "Properties": {
        "ApiId": {
          "Ref": "AppSyncApiId"
        },
        "Name": "LambdaDataSource",
        "Description": "Lambda data source",
        "Type": "AWS_LAMBDA",
        "ServiceRoleArn": {
          "Fn::GetAtt": ["LambdaExecutionRole", "Arn"]
        },
        "LambdaConfig": {
          "LambdaFunctionArn": "ここにLambdaのARNを記述"
        }
      }
    },
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": {
          "Fn::Sub": "AppSyncLambdaRole-${env}"
        },
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": "appsync.amazonaws.com"
              },
              "Action": "sts:AssumeRole"
            }
          ]
        },
        "Policies": [
          {
            "PolicyName": "InvokeLambdaFunction",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": ["lambda:invokeFunction"],
                  "Resource": "ここにLambdaのARNを記述"
                }
              ]
            }
          }
        ]
      }
    }
  },
```

注: lambda:invokeFunction のポリシーがないと、Lambda関数を呼び出すことはできません。

### 次はSESの設定です

トップページからIDの作成をして送受信できるメールアドレスを設定するだけです！

テスト段階ですとこれでいいですが本番になると使い物になりませんw

なので**次の課題としてRoute53でカスタムドメインでできるように調べて実装したいと思います！**

本番環境に移行する際にはドメインの設定が必要になることを念頭においてください！

最後にLambdaの関数がしっかりと使えるかテストします

mockやらaws lambda invokeなどCLIでもできますが

私はAWS Lambdaコンソールの作った関数を選択して行いました。

**リクエスト**
![リクエストJSON.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124781/38a81f5c-ab15-9a78-767d-0b8171e6fdc9.png)
**レスポンス**
![レスポンスJSON.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124781/1831f10a-facb-db6a-fb5d-06d4f5f0d6ad.png)


隠されたアドレスは自分がAWS SESコンソールで設定したアドレスです！
この関数を実行した段階でAWS SESとうまく連絡が取れていたらメールが受信するはずです！(toもfromもどっちも自分)

![メール受信と送信.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124781/83c4f8bb-7c07-cd94-5caf-3583770d7255.png)
これからもっと表示を華やかにしていけたらなと思います。

## 最後に

コード等は今回は載せませんが個人的に手順さえ残しておけば、おそらく未来の自分が過去の自分ナイス！ってなると思ったので残しておきます。

今回使ったGraphqlやDynamoDB AWS AmplifyとApp SyncとLambdaやSESと言うサービスや技術は全て初めてやることでした。

メール送信機能実装(schema作成とAPI作成も)はエンジニア完全未経験から初入社1日目にしていきなり下さったタスクです!

まだまだ開発途中ですが、必ず作り切る！！❤️‍🔥

反省点として、いきなりやりたい事を検索するのではなく(網羅的にザーッとなら別)
1つ1つのサービスや技術で何ができるのかをしっかりと理解して点を作ってから線を引くように実装していくのがいいと思いました！

駆け出しエンジニアの方に少しでも助けになれば幸いです！！

### 参考にした本です

https://www.amazon.co.jp/AWS-Amplify-Studio%E3%81%A7%E3%81%AF%E3%81%98%E3%82%81%E3%82%8B%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89-%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E7%B5%B1%E5%90%88%E9%96%8B%E7%99%BA-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83/dp/489977530X