フルスタックじゃなくても大丈夫！アプリケーション開発者の知識だけで作るAWS Amplifyを使ったお手軽サービス開発

こんにちは。アマゾン ウェブ サービス ジャパンのパートナーソリューションアーキテクトの宇賀神です。

この記事は [Japan APN Ambassador Advent Calendar 2020](https://qiita.com/advent-calendar/2020/japan-apn-ambassador) という APN  Ambassador[^1]の皆様にご参加いただいており、本記事はその13 日目です。

なお、本投稿内容につきましては、私個人の意見であり、所属企業・部門見解を代表するものではありません。

# はじめに
「プログラミングはある程度できるが、いざ趣味や副業でサービス開発を始めてみようとするとサーバー構築などのインフラの知識も必要で何から始めていいかわからない。覚えることが多すぎる…。」
そんな、アプリケーション開発の知識はありつつもインフラの知識が少ない方に向けて、新卒で入社したSIerではアプリケーションエンジニアをしていた私が、簡単に業務時間外でも２週間程度でサービス開発できた話を書いていきます。（ほとんどの時間はElectronでのデスクトップアプリ開発に使った…。）

今回はバックエンドの開発にAWS Amplifyを使うことで、簡単にアーキテクチャを構築できました。
サーバレスアーキテクチャにしており、インフラの管理は必要なく、サービス仕様に注力できます。
本記事ではどんなアーキテクチャでサービスを構成したかとざっくりとした手順についてお話します。

# どんなサービスを作ったか
最近オンラインイベントが増えているので、イベントの参加者が発表者に対して手軽に「いいね！」を送れるサービスを作りました。
参加者がブラウザでページにアクセスして、そこに表示されている絵文字をクリックすると、発表者のデスクトップ上に「いいね！」がに表示されます。

アプリはこちらからダウンロード可能で無料でご利用いただけます。
https://github.com/syobochim/ClapHandApp

![claphand.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48812/47eb63fc-0f21-4359-d6d1-7ed484ce67e4.gif)

# アーキテクチャと選定理由
アーキテクチャは以下の通りです。全てサーバレスなサービスを活用することでインフラの管理・運用が不要になり、サービスの要件に注力した開発ができました。

![20201213_Qiita_APNアドカレ.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48812/db11e4c1-a3a8-7729-5510-934a0754d989.jpeg)

全体のアーキテクチャの構築には AWS Amplify を利用しました。
AWSのサービス説明には、 AWS Amplify の説明に以下のように書かれており、アプリエンジニアが活用しやすいサービスになっています。
> AWS Amplify は、**モバイルとウェブのフロントエンドデベロッパー**が、安全でスケーラブルなフルスタックアプリケーションを構築しデプロイできるようにする、AWS による製品およびツールのセットです

AWS Amplify フレームワークは ライブラリ、UIコンポーネント、CLIツールチェーンの3つの要素を持っています。詳しくは[こちら](https://docs.amplify.aws/start/q/integration/js)。
今回、私は Amplify CLI を主に利用してバックエンド側のサービスを構築しています。実際に、YAMLファイルやJSONファイルはほぼ触らず、サーバー構築もせず、アプリケーション開発の知識だけでサービス作成ができました。[^2]

アーキテクチャの選定理由を３つの要素に分けて記載します。

## 1. AWS AppSync と Amazon DynamoDB で GraphQL を作成
サービスの主力機能となる「イベント管理」は AWS AppSync と Amazon DynamoDB を利用しました。
サービス選定時に、まずGraphQLを利用しようと考えました。その理由は以下の通りです。

- サービスの要件を作りながら考えていくために、GraphQLのクエリの柔軟性を利用したかった
- リアルタイムに発表者のデスクトップに通知を送る機能が、GraphQLの購読/subscriptionの機能を使えば簡単に実現できると考えた
- GraphQLを触って学びたかった

そこで、GraphQLの完全マネージドサービスである AWS AppSync を利用しました。
AWS Amplify と AWS AppSync を組み合わせることによって、APIのクエリの定義ファイル(`schema.graphql`）を変更すれば、サーバー構築どころか、プログラミングすら必要なくGraphQLのエンドポイントからAPIを利用できるようになりました。

## 2. Amazon API Gateway と AWS Lambda で REST API を作成
参加者からの「いいね！」の送信には Amazon API Gateway と AWS Lambda を利用しました。
今回はユーザーにログインさせずに気軽に利用できるサービスにしたかったので、APIの認証にはAPI Keyを利用しており、かつ、発表者（イベントの作成と「いいね！」の購読）と参加者（「いいね！」の送信）のアクセス権限を分けたかったので、サービスの制約上[^3]、結果としてAPIの入り口を発表者用の AWS AppSync と Amazon API Gateway で分けることにしました。

AWS LambdaにNode.jsを利用してリクエストに対する処理を実装していくことでREST APIのエンドポイントを用意できました。アプリケーションサーバーの構築から解放され、サービス仕様に注力できます。

## 3. Amazon CloudFrontとAmazon S3で静的ファイルのホスティング
参加者がブラウザでアクセスする静的ファイル（HTML / CSS / JavaScript）のホスティングには Amazon S3 と Amazon CloudFront を利用しました。 Amazon CloudFront を利用することで、コンテンツをキャッシュしてくれてクラウドのコスト削減やアクセス速度の向上に役立ちます。

HTML / CSS / JavaScriptを用意するだけでいいので、ウェブサーバーを敢えて構築する必要もありません。

# 開発・構築の流れ

開発と構築のざっくりとした流れをアーキテクチャの選定理由の記載に合わせて以下のように分類して説明します。
基本的には右に記載したコマンドを実行してサーバレスサービスの構築・設定をして、その後に要件に合わせてプログラミングを進めていくという流れになります。

1. AWS AppSync と Amazon DynamoDB で GraphQL を作成  →　`amplify add api` の後で `GraphQL` を選択
2. Amazon API Gateway と AWS Lambda で REST API を作成 → `amplify add api` の後で `REST` を選択
3. Amazon CloudFrontとAmazon S3で静的ファイルのホスティング　→ `amplify add hosting`

基本的には Amplify CLI を使ってバックエンドを構築し、そこから仕様に合わせてアプリケーションコードを書いていくという流れで開発を進めていきました。

## 0. AWS Amplifyのセットアップ

まず最初は AWS Amplify のセットアップをします。AWSのアカウントはご用意いただき、以下の通りインストールと設定を完了させます。[^4]

```bash
$ npm install -g @aws-amplify/cli
$ amplify configure
```

コマンドを入力すると対話型で利用するリージョンやAWS Amplifyが利用するIAMユーザーの名前などが聞かれます。途中、AWSの管理コンソールが起動してユーザー作成の操作が必要になります。

初期設定が完了したら、以下のコマンドを実行してAWS Amplifyの雛形プロジェクトを作成します。コマンドを実行すると対話型のインターフェースになり、 JavaScript / iOS / Android などのアプリのビルド対象や、Angular / React / Ionic / Vue などのJavaScriptフレームワークを選択できます。

```
$ amplify init
```

実行するといくつかのファイルやフォルダができますが、まだ何も触らなくてOKです。

### AWS Ampilfyでよく使ったコマンド
さて、ここから AWS Amplify を使ってバックエンドサービスを構築していきますが、私がサービス開発で主に利用したコマンドはこちらです。

- `amplify <category> add` : `<category>`に指定したリソースを追加する
- `amplify <category> remove` : `<category>`に指定したリソースを削除する
- `amplify push` : ローカルの変更に合わせてクラウドのリソースをアップデートする。
- `amplify publish` : ローカルの変更に合わせてクラウドのリソースをアップデートする。ホスティングしているファイルを更新する。
- `amplify pull` : クラウドのリソースをローカルの設定ファイルに適用する
- `amplify status` : ローカルリソースを変更してクラウドに適用されていない者を表示する

なお、コマンドの一覧は[こちら](https://docs.amplify.aws/cli/start/workflows)から確認できます。

## 1. AWS AppSync と Amazon DynamoDB で GraphQL を作成

ではいよいよサービス部分を作っていきます。
AWS AppSync と Amazon DynamoDB を構築するために、以下のコマンドを実行します。こちらもコマンドを実行すると対話型でAPIの名称などが聞かれます。`>` の後ろが入力・選択した値です。
authorization typeは、利用者にユーザー登録させる場合は `Amazon Cognito User Pool` を利用するのがお手軽だと思いますが、私のアプリは誰でもアクセスできるようにしたかったので `API key` を選択しました。

```bash
$ amplify add api
? Please select from one of the below mentioned services: 
> GraphQL
? Provide API name:
> myapp
? Choose the default authorization type for the API 
> API key
? Enter a description for the API key: 
? After how many days from now the API key should expire (1-365): 
> 7
? Do you want to configure advanced settings for the GraphQL API
> No, I am done.
? Do you have an annotated GraphQL schema? 
> No
? Choose a schema template: 
> Single object with fields (e.g., “Todo” with ID, name, description)
```

実行すると、`amplify`> `backend` > `api` フォルダに先ほどAPI nameで指定した名前のフォルダができています。
ここで変更するのは基本的に`schema.graphql` ファイルのみです。このファイルを使ってGraphQLのAPIを作成していくことができ、POSTしたデータは自動で Amazon DynamoDB に保存されます。
なお、私は最終的に要件上 `resolvers`フォルダ内にファイルを追加したりもしました。

```
.
├── amplify/
│   ├── #current-cloud-backend/
│   ├── backend/
│   │   ├── amplify-meta.json
│   │   ├── api
│   │   │   └── myapp
│   │   │       ├── build/
│   │   │       ├── parameters.json
│   │   │       ├── resolvers/
│   │   │       ├── schema.graphql
│   │   │       ├── stacks/
│   │   │       └── transform.conf.json
│   │   ├── backend-config.json
│   │   └── tags.json
│   ├── cli.json
│   └── team-provider-info.json
└── src/
    └── aws-exports.js

```

`$ amplify push` を実行すると、AWSの環境に AWS AppSync と Amazon DynamoDB` が構築されます。
実行の最後にGraphQLのendpointのURLとAPI Keyが表示され、構築された環境にアクセスできます。

```bash
$ amplify push
```

私は以下のように `@key` を使って追加のクエリを用意したり、 `type` を追加して `Mutation`（追加・変更・削除用）や `Subscription`(WebSocketによる購読)を追加しました。

```graphql:schema.graphql
type Clap
  @model(subscriptions: null)
  @auth(rules: [{ allow: public }])
  @key(name: "ByEventName", fields:["event"], queryField: "listClapsSortedByEvent")
{
  type: String!
  id: ID!
  owner: String
  event: String
  count: Int!
  emoji: String!
  timestamp: AWSTimestamp!
}

type Subscription {
  onUpdateClapCount(id: ID): Clap  @aws_subscribe(mutations: ["updateClapCount"])
}

type Mutation {
  updateClapCount(id: ID!) :Clap
}
```

## 2. Amazon API Gateway と AWS Lambda で REST API を作成

次に Amazon API Gateway と AWS Lambda 部分を作成していきます。こちらも `amplify add api` コマンドを利用します。 `>` の後ろが入力・選択した値です。
ここで AWS Lambda の関数名や利用するプログラミング言語、テンプレートなどを選択していきます。Node.jsを選択した場合、テンプレートにはexpressの利用なども選択できます。
個人的によかったのが `Do you want to access other resources in this project from your Lambda function? ` の質問で、`Yes`を選択して先ほど作成したGraphQLのAPI名を指定すると、 AWS AppSync へアクセスするための情報を AWS Amplify が AWS Lambda の環境変数に自動で適用してくれます。認証情報などをファイルで持つ必要がなくなり、ソースコードの管理がしやすくなりました。

```bash
$ amplify add api
? Please select from one of the below mentioned services:
> REST
? Provide a friendly name for your resource to be used as a label for this category in the project:
> myappAPI
? Provide a path (e.g., /book/{isbn}):
> /items
? Choose a Lambda source 
> Create a new Lambda function
? Provide a friendly name for your resource to be used as a label for this category in the project:
> myappAPI
? Provide the AWS Lambda function name: 
> myappAPI
? Choose the runtime that you want to use: 
> NodeJS
? Choose the function template that you want to use: 
> Hello World
? Do you want to access other resources in this project from your Lambda function? 
> Yes
> api
> myapp
? Do you want to invoke this function on a recurring schedule? 
> No
? Do you want to configure Lambda layers for this function? 
> No
? Do you want to edit the local lambda function now? 
> No
? Restrict API access
> No
? Do you want to add another path?
> No
```

この操作で `amplify` > `backend` > `api` の中と `amplify` > `backend` > `function` の中に先ほど入力した名称のフォルダが生成されます。
ファイルがいろいろできますが、編集するのは `amplify/backend/function/myappAPI/src`フォルダの中だけでOKです。
Node.jsを選択した場合は、この中に`package.json`と`index.js`が格納されており、ここに業務処理を書いていきます。

```
.
├── amplify
│   ├── #current-cloud-backend/
│   ├── backend/
│   │   ├── amplify-meta.json
│   │   ├── api/
│   │   │   ├── myapp/
│   │   │   └── myappAPI/
│   │   │       ├── api-params.json
│   │   │       ├── myappAPI-cloudformation-template.json
│   │   │       └── parameters.json
│   │   ├── backend-config.json
│   │   ├── function/
│   │   │   └── myappAPI
│   │   │       ├── amplify.state
│   │   │       ├── function-parameters.json
│   │   │       ├── myappAPI-cloudformation-template.json
│   │   │       ├── parameters.json
│   │   │       └── src/
│   │   └── tags.json
│   ├── cli.json
│   └── team-provider-info.json
└── src/
```

変更できたら以下のコマンドを実行します。すると、Amazon API GatewayとAWS Lambdaが構築されます。

```bash
$ amplify push
```

## 3. Amazon CloudFrontとAmazon S3で静的ファイルのホスティング

最後にAmazon CloudFrontとAmazon S3を構築していきます。こちらは `amplify add hosting` コマンドを利用します。 `>` の後ろが入力・選択した値です。
ホスティング方法にAmplify Consoleでホスティングするか S3とCloudFrontでホスティングするかという選択が出てきます。
私はどういう状態になっているか確認したかったのでS3とCloudFrontを選択しました。しかし、こちらを選択すると毎回静的コンテンツを更新した時にCloudFrontのキャッシュの削除が必要になってしまいます。
Amplify Consoleでのホスティングでは、デプロイ時にキャッシュの削除も自動で実施してくれるので、こちらの方が管理の手間はかからないと思います。

```bash
$ amplify add hosting
? Select the plugin module to execute
> Amazon CloudFront and S3
? Choose a type
> Manual deployment
```

このコマンドを実行すると `amplify` > `backend` > `hosting` というフォルダが生成されます。

```
.
├── amplify
│   ├── #current-cloud-backend/
│   ├── backend/
│   │   ├── amplify-meta.json
│   │   ├── api/
│   │   ├── awscloudformation/
│   │   ├── backend-config.json
│   │   ├── function/
│   │   ├── hosting/
│   │   │   └── amplifyhosting/
│   │   │       └── amplifyhosting-template.json
│   │   └── tags.json
│   ├── cli.json
│   └── team-provider-info.json
└── src/
```

私はルートディレクトリ（amplify や src と同じ階層）に `public`フォルダを用意し、そこに `index.html` を用意し、 `src` フォルダ内にReactにて利用する JS / CSSファイルを配置しました。
また、プロジェクトのルートディレクトリに `package.json` を用意し、Reactを利用するための設定を行いました。

変更できたら以下のコマンドを実行します。すると、Amazon S3とAmazon CloudFrontが構築され、静的ファイルがホスティングされます。静的ファイルの更新は `amplify push` ではできないので注意してください。

```
$ amplify publish
```

# 最後に

このように、Amplify CLIの利用とアプリケーションエンジニアに必要となる知識でサービス開発ができました。
皆さんも是非、AWS Amplifyを使ってお手軽にサービス開発をしてみてはいかがでしょう！
AWS Amplifyについてさらに学びたい方には、こちらのGitHub RepositoryにAWS Amplifyの日本語コンテンツがまとめられているので、お役にたつのではと思います。
https://github.com/aws-amplify-jp/awesome-aws-amplify-ja

幅広い範囲の知識を得ることは、より安全に品質の良いサービスの作成につながります。
サービス開発を進めていく中で気をつけておきたいポイントについては[ホワイトペーパー](https://aws.amazon.com/jp/whitepapers/)などを参照して拡充していくことをお勧めします！

[^1]: AWS Partner Network（APN）に加入している会社から卓越した技術力を持ち、社外への情報発信（セミナーでの発信、Blogや書籍での発信）をしているメンバーが、日本のみならず世界中のAPNパートナーの中から選出されるProgramです。
[^2]: node.js利用のためにpackage.jsonは触りました。
[^3]: AWS AppSyncの制約。メインまたはデフォルトの承認タイプとして API_KEY または AWS_IAM を指定した場合、それを追加の承認モードの 1 つとして再び指定することはできません。（https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/security.html ）
[^4]: 参考：https://docs.amplify.aws/cli/start/install