AWS Amplifyでメモアプリを構築


今回は、AWS公式の以下チュートリアル/ハンズオンを実施し、Amplifyを使ったメモアプリを構築したので、その内容を説明します。
本記事の多くは以下2つの記事の解説/引用となっています。
> AWS Hands-on for Beginners AWS Amplify を用いたWebサイトの構築方法を学ぼう
https://aws.amazon.com/jp/blogs/news/aws-hands-on-for-beginners-19/
> Amplify Dev Cnter > Docs （チュートリアル）
https://docs.amplify.aws/start/q/integration/react/

チュートリアルは全て英語で書かれていますが、本記事は日本語で記載し、適宜自分なりの理解を付け加えています。
受講理由は、Amplifyを用いたアプリの開発を検討しているためです。


## 本日のお品書き
- AWS Amplifyってなに？
- ハンズオンの特徴
- ハンズオン内容
- 【おまけ】番外編
- むすび


## AWS Amplifyってなに？
AWSの公式は以下のように説明しています。
> AWS Amplify を使用すると、開発からデプロイまで、機能が豊富なフルスタックのウェブアプリケーションとモバイルアプリケーションの構築がこれまでになく簡単になります。ビジネスの成長に合わせて、迅速に市場に参入し、規模を拡大します。
https://aws.amazon.com/jp/amplify/


### 何が良いの？どんなことができるの？

- サーバレスなバックエンドを自動で構築してくれる。
    1. AWS Cognitoを用いた認証基盤
    1. AppSync、APIGateway&LambdaによるGraphQL/REST APIのエンドポイント
    1. DynamoDBなどのDB

    などなど。

- フロントからバックエンドのconfig/codeの作成
    1. Reactなどのライブラリ/UIコンポーネント等の開発に必要な共通機能が提供されているクライアントフレームワーク
    1. フレームワークによる手軽な反映

まとめると・・・
Amplifyとは、AWS用いたサーバレスバックエンドを構築するためのCLIやバックエンドと接続するためのクライアントライブラリ、Webサイトホスティングを提供するサービスとなります。
アプリケーション開発に必要な一般的な技術要素をゼロから構築するのは重労働です。開発者は、差別化のできる本来のアプリケーションの実装に集中したいですよね。そのため、それらを隠蔽したように提供してくれるAmplify使えば楽なんじゃない？となるわけです。Amplifyにはできないことも当然存在するため、万能ではないことには留意する必要があります。

なんだかすごそうですね、うん、すごいということはわかりました。

## ハンズオンの特徴
- 日本語による解説。
- プログラミング言語はReact。
- 東京リージョン(ap-northeast-1)
- AWS Cloud9という統合開発環境を使ってるため、受講者の環境に依存せずブラウザだけあれば実践できる。
- リソースの削除まで解説有り。（削除忘れなどがなくて良いね
- スライドをダウンロード可能
- 所要時間は2時間くらいなので手軽。
- 無料枠が適用されているならお金かからない。


## ハンズオン内容
ハンズオンでは、以下のサービス/構成を構築します。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754596/355a66b3-ad60-c253-2d5b-b5352b9602b7.png)

各種サービスの役割を説明します。

- Cognito：ユーザの認証基盤(サインインやサインアップ)を提供する
- AppSync：GraphQLを用いたAPIエンドポイントを提供する
- DynamoDB：NoSQLなデータベース、本構成ではAppSync経由でデータの読み書きが行われる
- Amplify：上記を扱うためのプラットフォーム、また、Webホスティングに用いる
- Cloud9：構成図には載っていないが、統合開発環境としてエディタおよびCLIを提供する

### 構築手順
本項では構築手順を示します。
基本的には、チュートリアルに記載の内容で問題なくデプロイまでできると思います。
そのため、細かな説明は割愛します。言語はハンズオンにあるようにReactを選択しました。
Cloud9では、npmコマンドが使えますが、ローカルで実行する場合は、npmコマンドを使うための環境構築を先に実施してください。また、CLIコマンドの入力に対して対話式に設定が進んでいきます。適宜チュートリアル・ハンズオンを参考にしてみてください。

#### １. Prerequisites

ここでは、Amplify環境の準備を行います。
以下を実行します。
```terminal
$ npm install -g @aws-amplify/cli
```
次に、amplify configureを実行して、amplifyで開発を行うら目のプロファイルを作成します。
```terminal
$ amplify configure
```
本コマンドを実行すると、IAMユーザを作成するためのURLが表示されるので、アクセスしてユーザを作成し、アクセスキーIDとシークレットアクセスキーを入手します。
その後ターミナルに戻り、以下の項目で入手したキーを入力していきます。
```terminal
? accessKeyId:  <ACCESSKEYID>
? secretAccessKey:   <ACCESSKEY>
```

#### ２. Set up fullstack project
つづいて、Amplifyプロジェクトのバックエンドとフロントエンドの初期化設定をしていきます。
まずは、reactを使えるようにします。
```terminal
$ npx create-react-app react-amplified
```
cdで作成されたフォルダに移動して、npm startコマンドでサーバを起動します。
```terminal
$ cd react-amplified
$ npm start
```
実行後、Cloud9のヘッダー部分に存在するPreviewを押下し、Preview Running Applicationを選択し、ブラウザを表示します。
以下のような画面が確認できれば、サーバの起動が成功です。なお、表示されている画面はcreate-react-appにデフォルトで書かれているものです。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754596/cb9f0638-3d98-34af-8ae8-6ac7a7422793.png)

ここまできたら、initコマンドで、amplifyの初期化設定を実施します。
```terminal
$ amplify init
```
実行すると、アプリに関する情報の入力をいろいろ求められるので入力していきます。

続いて、amplifyのライブラリをインストールします。
```terminal
$ npm install aws-amplify
```
バックエンドサービスと対話できるように設定を行います。Cloud9のサイドバーからsrc/index.js を開き、次のコードを追記します。importが書かれたまとまりの下に追加すれば良いです。
```terminal
$ import { Amplify } from 'aws-amplify';
$ import awsExports from './aws-exports';
$ Amplify.configure(awsExports);
```
これによって、ReactアプリケーションはAmplifyのバックエンドと通信できるになりました。

#### ３. Connect API and database to the app
本項では、APIのデプロイとフロントエンドからの接続の実装を行います。
amplify add apiを実行してGraphQL APIを作成します。REST APIを選らんでも実施できますが、本ハンズオンではGraphQL APIを使用しています。/react-amplifiednpx/amplify/backend/api/reactamplifiednpx/schema.graphqlをみると、スキーマが記述されていることがわかります。ここに例えばmailaddressなどの項目を追記することで、要素をふやしたりできます。
```terminal
$ amplify add api
```
```react:schema.graphql
type Todo @model {
  id: ID!
  name: String!
  description: String
}
```
ただし、amplify add apiだけではamplify上に設定が反映されていない状態なので、以下のコマンドを実行してあげます。

以下のコマンドで、Amplifyバックエンドをデプロイして設定を反映してあげます。バックエンドはDynamodbをGraphQLで操作(読み書き)します。
```terminal
$ amplify push
```
以下コマンドで、Amplifyプロジェクトのステータスが確認できます。実行後に出力されるものを見てみると、CategoryにApiが追加されていることがわかります。
```terminal
$ amplify status
```
ここまでできたら、Connect frontend to APIと題して、作成したAPIにアクセスするフロントエンドの実装を行います。ここではsrc/App.jsを編集していきます。App.jsに記載されている元のコードを削除して、以下のコード挿入してください。
```react:App.js
/* src/App.js */
import React, { useEffect, useState } from 'react'
import { Amplify, API, graphqlOperation } from 'aws-amplify'
import { createTodo } from './graphql/mutations'
import { listTodos } from './graphql/queries'

import awsExports from "./aws-exports";
Amplify.configure(awsExports);

const initialState = { name: '', description: '' }

const App = () => {
  const [formState, setFormState] = useState(initialState)
  const [todos, setTodos] = useState([])

  useEffect(() => {
    fetchTodos()
  }, [])

  function setInput(key, value) {
    setFormState({ ...formState, [key]: value })
  }

  async function fetchTodos() {
    try {
      const todoData = await API.graphql(graphqlOperation(listTodos))
      const todos = todoData.data.listTodos.items
      setTodos(todos)
    } catch (err) { console.log('error fetching todos') }
  }

  async function addTodo() {
    try {
      if (!formState.name || !formState.description) return
      const todo = { ...formState }
      setTodos([...todos, todo])
      setFormState(initialState)
      await API.graphql(graphqlOperation(createTodo, {input: todo}))
    } catch (err) {
      console.log('error creating todo:', err)
    }
  }

  return (
    <div style={styles.container}>
      <h2>Amplify Todos</h2>
      <input
        onChange={event => setInput('name', event.target.value)}
        style={styles.input}
        value={formState.name}
        placeholder="Name"
      />
      <input
        onChange={event => setInput('description', event.target.value)}
        style={styles.input}
        value={formState.description}
        placeholder="Description"
      />
      <button style={styles.button} onClick={addTodo}>Create Todo</button>
      {
        todos.map((todo, index) => (
          <div key={todo.id ? todo.id : index} style={styles.todo}>
            <p style={styles.todoName}>{todo.name}</p>
            <p style={styles.todoDescription}>{todo.description}</p>
          </div>
        ))
      }
    </div>
  )
}

const styles = {
  container: { width: 400, margin: '0 auto', display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: 20 },
  todo: {  marginBottom: 15 },
  input: { border: 'none', backgroundColor: '#ddd', marginBottom: 10, padding: 8, fontSize: 18 },
  todoName: { fontSize: 20, fontWeight: 'bold' },
  todoDescription: { marginBottom: 0 },
  button: { backgroundColor: 'black', color: 'white', outline: 'none', fontSize: 18, padding: '12px 0px' }
}

export default App
```

更新後、サーバを起動し、変更内容が反映されているかを確認します。
サーバを起動したらブラウザを開いて確認します。入力画面のようなものがあればokです。入力欄に文字を入れてボタンを押すことで動作します。
```terminal
$ npm start
```

#### ４. Add authentication
本項では、作成したアプリに認証機能を実装します。以下のコマンドを実行してください。
```terminal
$ amplify add auth
```
デプロイします。
```terminal
$ amplify push
```
ログインUIを作ります。パッケージがあるので便利です。
```terminal
$ npm install @aws-amplify/ui-react
```
コードを変更します。変更はsrc/app.jsに加えます。
① まずは、importの下あたりに以下を追記します。
```react:App.js
import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
```
② 次に、以下の行を書き換えます。
```diff_javascript:App.js
- const App = () => {
+ const App = ({ signOut, user }) => {
```
③ returnの部分にも以下2行を挿入します。
```diff_javascript:App.js
return (
 <div style={styles.container}>
+   <Heading level={1}>Hello {user.username}</Heading>
+   <Button onClick={signOut}>Sign out</Button>
    <h2>Amplify Todos</h2>
```
④ 最下部に記載されているexportを変更してあげます。
```diff_javascript:App.js
- export default App
+ export default withAuthenticator(App)
```

<br><br>

#### ５. Deploy and host app
最後にAmplifyアプリのデプロイを実施します。といってもめちゃくちゃ簡単です。htmlなどをamplifyにホスティングするための設定です。
```terminal
$ amplify add hosting
```
また、pushと同様に、addだけでは設定は反映されません。クラウドリソースへの変更を行なった上で、webアプリケーションを手元でビルドし、Amplifyコンソールでホスティングされたアプリケーションを更新する必要があります。以下を実行します。
```terminal
$ amplify publish
```
完成したものはこんな感じです。認証フォームとログイン後の画面になります。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754596/d8b06b2d-6469-3faf-a995-474f1a6ee880.png)
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754596/56054199-d20f-428c-1c8f-74872a4ea26a.png)


これで、作成したアプリを公開することができました。
コマンドを実行すると出力されるURLをコピペでChromeなどで検索してみてください。作成したアプリが見えれば成功です。

ハンズオンでは、リソースを削除してくれるところまで公開してくれているので、参考にしてみてください。

## 【おまけ】番外編
以下の記事にAmplify authのcognitoを使ったログイン機能を少し改変し、クライアントからはユーザ登録できないようにしました。できるのはサインインのみです。これは管理者がマネジメントコンソールからアカウントをユーザプールに予め登録するような運用で、サービスの利用範囲を限定したい時に便利です。興味がある方は是非見てみてください。
> 【Amlify】Cognitoの認証フォームのサインアップを無効にする方法
https://qiita.com/ayumu__/items/c917f31b09c1ecedaf45

## むすび
- 本記事では、Amplifyのチュートリアルとハンズオンを紹介しました。
- 本講座の受講目的はAmplifyを用いたWeb開発に活用することにあるので、これを礎にプロダクトを開発していきたいと思います。
- 開発したものは、そのうち記事にしたいと思います。

それでは。