Nuxt3で作成したWebページをAWS Amplifyでデプロイする

# エンジニアもすなるクラウドといふものを、私もしてみむとてするなり
今まで過去記事で作成したWebページやら個人でテスト用に作ってみたサイトやら死蔵されたWebページを数知れず…  

ふとパッケージテストのために作った数多くのWebページを見返していたら、そういえば一度も公開というものをしたことがなかったなあと思いになりました。
フロント・バックエンドのコーディングの経験値をいくら積もうとも、一度もサーバーをいじってみたり、Webページを公開をしない、クラウドを全然触ったことがないでエンジニアと言えるだろうか！いや、言えない！（反語！！！）
  
ということで、試しに一度くらいはクラウドを触ってみよう！ 
かといって目的もプログラミング学習が何の意味もないように、やることも決まってないのにクラウドなんか触る物じゃないので、以前Nuxt3のテストで作ったしょっぱいWebページをAWSにデプロイしていきたいと思います。

# 
さてAWSでWEBページを公開しようと決意をするも、AWSスクール一日生の私としてはどうすればデプロイできるのか、まずどんな手段があるのかすらわかりません。
そこで本格的な調査は別として軽くどんなものがあるか教えてもらおうとChatGPT君に、AWSでWEBページを作るにはどんな手段があるか聞いてみたら以下の5つが挙げられました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/651c942f-3310-99d8-25eb-612e711f1f88.png)

ふむふむ、Amazon S3とAmazon EC2は聞いたことがありますね。
しかし説明を読む限り、今回は動的WEBページをデプロイしたいのでAmazonS3は選択肢から除外。そうなると残りは2~4に限られますが、EC2は柔軟性が高いのでしょうが、環境を自分でいろいろ設定しないと行けなそう。一度にいろいろやっても脳内CPU使用率が100％を張り付いたまま動かなくなるので今回はパス。残りは3~5ですね。
今回デプロイしたいのはNuxt3で作成したWEBページ。そうなると`フレームワーク（React、Angular、Vueなど）で構築されたウェブアプリケーションを素早くデプロイできます`このような如何にも需要に合致した説明がされたならこれを選ぶしかないように思えてくる…。

しかも[AWS Amplifyのページ](https://aws.amazon.com/jp/amplify/)を覗くと、でかでかとこのような文章が！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/20045e7c-e0ed-ecba-db6d-90f24bff624b.png)

し、信じていいんですか…？Amazonさん…？
自慢ではないですが、手順書通りに環境構築しても一発で成功したためしがないこの私が、書いてある通り簡単にできるんですか…？

# 実践編
うだうだ悩んでいても埒が明かない！何事も実践！ダメだったら次に切り替える！
ということで、Amazonさんが用意してくれた[チュートリアル](https://docs.amplify.aws/react/start/)に沿ってAmplifyにデプロイしていきたいと思います。

## Amplify環境構築
チュートリアルを読み進めていくと`Before you begin, make sure you have the following installed:`という文言とともに以下のパッケージのバージョンが書かれています。
```
Node.js v14.x or later
npm v6.14.4 or later
git v2.14.1 or later
```
いずれも古いバージョンなので大丈夫だと思いますが、一応確認しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/34a970eb-98c6-004c-c27f-b98ad959f5af.png)

さて、バージョン確認が終わったいよいよここからが下準備に入ります。以下のコマンドを打ち込んでAmplifyCLIをインストールします。
```
npm install -g @aws-amplify/cli
```

それが終わりましたら、以下のコマンドでAmplify CLI の設定を行っていきます
```
amplify configure
```
コマンドを打つとAWSのログイン画面に飛ばされるので、そこからログインを行い、Enterで次へ。

```
Sign in to your AWS administrator account:
https://console.aws.amazon.com/
Press Enter to continue
```

リージョンを聞かれるのですが、どこでもいいので適当にap-northeast-1で次へ
```
Specify the AWS Region
? region:  ap-northeast-1
Follow the instructions at
https://docs.amplify.aws/cli/start/install/#configure-the-amplify-cli
```

そうしたらAWS consoleでユーザー作れよと言われますが、適当にユーザーを作成し、`AdministratorAccess-Amplify`ポリシー追加してEnterで次へ
```
to complete the user creation in the AWS console
https://console.aws.amazon.com/iamv2/home#/users/create
Press Enter to continue
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/5e25a3f3-b13f-4476-33b7-a1b77764fe8e.png)


そうするとaccessKeyとsecretAccessKeyが要求されるので、入力したらEnterでセットアップ完了！
```
Enter the access key of the newly created user:
? accessKeyId:  # YOUR_ACCESS_KEY_ID
? secretAccessKey:  # YOUR_SECRET_ACCESS_KEY
This would update/create the AWS Profile in your local machine
? Profile Name:  # (default)
Successfully set up the new user.
```

## コードをデプロイ
Amplifyを使う下準備が完了しましたので、今回デプロイしたいコードのrootディレクトリまで移動し、以下のコードを実行します。
```
amplify init
```

いろいろずらっと表示されますが特に変更はないのでYesで次へ行き、処理完了を待ったらinitは終了です
```
The following configuration will be applied:

Project information
| Name: #適当なプロジェクト名
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: vue
| Source Directory Path: src
| Distribution Directory Path: dist
| Build Command: npm run-script build
| Start Command: npm run-script serve

? Initialize the project with the above configuration? Yes
```

ここまで来たらようやく本番。以下のコマンドを実行します。

```
amplify add hosting
```

そうしたら二回選択肢が出てくるので、それぞれ以下の通りに選択します（両方上の選択肢）

```
Select the plugin module to executeHosting with Amplify Console (Managed hosting with custom domains, Continuous deployment)
Choose a type: Manual Deployment
```

ようやく大詰めを迎えました。あとは以下のコマンドが通れば完了です！
```
amplify publish
```

無事通って、URLが表示されました！
ここまでの手順で済むなんてメチャクチャ簡単ですね！

## そして404へ…
さて、WEBページの公開が済んだところで、ちゃんと動くか試してみましょう！
まあ、エラーも出てないですし確認して終わりでしょう！

![スクリーンショット 2023-12-07 020140.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/39e38b2a-f2e5-c0ec-c3fc-36fd4c694eb6.jpeg)

**？？？？？？？？？？？？？？？？**
なんで？なんで？なんで？![EhVhYp6VgAAnH42.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/06f48e37-2450-091e-0b8b-a96bf13a520a.jpeg)

## 原因編
こういうときはまずは偉大なる先人の知恵があるはず、とネットの海を潜るとドンピシャなページを発見！

https://zenn.dev/aoito/articles/7df48f8e0dc365

この記事によると`Nuxt3のビルドコマンドは npm run generate ですので、書き換えてください。`とのこと。
確かにNuxt3でビルドコマンド変わってたかも…
そしてどうやらamplify.ymlとやらを書き換えればいいらしい。またamplify.ymlはAWS Amplify画面の「アプリの設定」＞「ビルドの設定」というところから書き換えられるとのこと

なんだか割と簡単に解決できそうでよかったよかった


### amplify.yml is どこ！？
善は急げ！さっそく該当箇所を書き換えよう！
まずはAWSからAWS Amplifyを開いて…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/a8408fe6-aaa4-3c40-d3f4-78a3708f3d62.png)

**？？？？？？？？？？？？？？？？**
なくない？？？
え、じゃあローカルPCにあるとかかな？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/0c69cbbf-9f18-2d3b-488c-92b7c0fb44b8.png)

**？？？？？？？？？？？？？？？？**
**amplify.yml is どこへ！？**

### amplify.ymlなら荼毘に付したよ
amplify.ymlが見つからない…もしかして自分で追加するのか…？
それとも手順どこかでミスったのか…？
そんなこんなで作業を見返しながら、`amplify add hosting`で追加した際のコマンドにふと目をやると
```
The following configuration will be applied:

Project information
| Name: #適当なプロジェクト名
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: vue
| Source Directory Path: src
| Distribution Directory Path: dist
| Build Command: npm run-script build
| Start Command: npm run-script serve

? Initialize the project with the above configuration? Yes
```

```
| Build Command: npm run-script build
```
こいつかああああああ！


`amplify configure project `を打ち込んで、`Build Command`に辿り着くまで次の順番に選択していきます

```
? Which setting do you want to configure? Project information
? Enter a name for the project プロジェクト名   
? Choose your default editor: Visual Studio Code
√ Choose the type of app that you're building · javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  (npm.cmd run-script build) 
```

ビルドコマンドを上で示した通りに`npm run generate`に変更して`amplify publish`を再実行して、再度先ほどのURLを開くと…？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/d7ae710d-4a21-1390-9e9c-c706f16e7b31.png)

やったああああああああああああああ！
見慣れたチープなしょうもない画面来たああああああああああああああ！

第三部完！

# GraphQLの実装
WEBページをデプロイしたならば、このままバックエンドもAmplifyで実装してみたいと思います。
せっかくなので今回はお気に入り機能を追加します。

プロジェクトのルート階層から以下のコマンドを実行してGraphQLセットアップします
```
amplify add api
```
選択肢はチュートリアル通りGraphQL > Continue > Single object with fieldsを選択。
ここでデフォルトのスキーマが生成されるのですが、今回はスキーマのエディットはしないでおきます
```
? Select from one of the below mentioned services:
> GraphQL
? Here is the GraphQL API that we will create. Select a setting to edit or continue
> Continue
? Choose a schema template:
> Single object with fields (e.g., “Todo” with ID, name, description)
...
Edit your schema at <...>/schema.graphql or place .graphql files in a directory at <...>/schema
✔ Do you want to edit the schema now? (Y/n)
> no
```

上のコマンドで作成されたデータ群を以下のコマンドでamplifyに反映します
```
amplify push
```

## Nuxt3への実装
さて、ここまででAmplifyにGraphQLは設定できたので、続いてはNuxt3にデータの取得・登録処理を実装していきたいと思います。
以下のコマンドでnuxt3用のパッケージをインストールします。
```
npm install aws-amplify
```

そしてインストールが完了したら、pluginsフォルダに`amplify.ts`ファイルを作成して以下の記述を追加します。

``` amplify.ts
import { defineNuxtPlugin } from '#app'
import { Amplify } from '@aws-amplify/core'
import amplifyConfig from '@/src/aws-exports'
export default defineNuxtPlugin(() => {
  Amplify.configure(amplifyConfig)
})

```

それからGraphQLを使いたいページに以下のようにimportを追加します
```ts
import { generateClient } from 'aws-amplify/api';
import { createTodo, updateTodo, deleteTodo } from './graphql/mutations';
import { listTodos } from './graphql/queries';

const client = generateClient()
```

他の記事を見てると下のようにaws-amplifyからAPIをimportしてる記述が多かったのですが、私の環境でやるとapiなんてないぞって怒られて先に進めないので、代わりにgenerateClientを使っていきます。
```ts
import { API, graphqlOperation } from 'aws-amplify'
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/095c59cf-c02c-bd8d-9501-e975c91d6968.png)

今回は前回作った銭湯の個別ページにお気に入りボタンを用意して、処理を追加したいと思います（デザインガダサイノハ知ラナイ）。

処理はこのように上で追加したclientをつかってGraphQLが自動で生成した登録APIを投げます。
```ts
await client.graphql({
    query: createTodo,
    variables: {
        input: {
            id: queryPlaceId,
            name: spa.value.name
        }
    }
})
```

本当は名前含めてカラムやらtodoから書き換えるべきでしょうが、今回はplace_idとnameをそれぞれ登録できれば機能の再現をできるのと、あくまでGraphQLの疎通確認が目的なのでこのままいきましょう。


ともかくボタンの処理もできたのでお気に入りボタンを押してみましょう。
記述が正しければDynamoDBに登録されているはずですが…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/30723c81-cffe-1a58-c494-c4e19a2bc518.png)

登録できてる！デプロイと違ってすんなりいけました！


なら登録はできたので次は、取得APIの実装ですね。
今回も最初にGraphQLが自動生成した一覧取得APIを使います
```
await client.graphql({
      query: listTodos
    })
```

お気に入り一覧を表示する画面を作成します。

``` vue
<template>
  <div>
    <PageTitle :title="title"></PageTitle>
    <v-container>
      <v-row justify="center">
        <v-col cols="12" sm="8" md="6">
          <v-list>
            <v-list-item
              v-for="(item, index) in list"
              :key="index"
              class="mb-6"
              border
              height="60"
              lines="two"
              rounded
              ><NuxtLink :to="`/sauna/${item.id}`">
                <v-list-item-title>
                  {{ item.name }}
                </v-list-item-title>
              </NuxtLink>
              <template #append>
                <v-list-item-action>
                  <v-icon
                    @click="clickFavorite(item.id, item.name, item.isForvarite)"
                    >mdi-delete</v-icon
                  >
                </v-list-item-action>
              </template>
            </v-list-item>
          </v-list>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

<script lang="ts" setup>
  import { generateClient } from 'aws-amplify/api'
  import { createTodo, deleteTodo } from '../../src/graphql/mutations.js'
  import { listTodos } from '../../src/graphql/queries'

  const client = generateClient()
  const title = 'お気に入り'
  const list = ref([])

  const clickFavorite = async (placeId, placeName, isfavorite) => {
    if (isfavorite) {
      await client.graphql({
        query: createTodo,
        variables: {
          input: {
            id: placeId,
            name: placeName
          }
        }
      })
    } else {
      await client.graphql({
        query: deleteTodo,
        variables: {
          input: {
            id: placeId
          }
        }
      })
    }
  }

  onMounted(async () => {
    useHead({ title })
    const res = (await client.graphql({
      query: listTodos
    })) as any
    list.value = res.data.listTodos.items
  })
</script>

<style scoped></style>

```

画面を開くと…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684225/677dd8df-0d0c-d9ac-fc07-46836cb11e6a.png)

こっちもデータを取得できました！

# あとがき
ちょっとした躓きはありましたが、AWS Amplifyの公式ページで書いてあった通り、数時間で実装することができました！
なんならnuxt3のbuild command問題が消費時間の大半で、最初からnuxt3は特別だって気づけていれば一時間もかかりませんでしたね…

一応実装に成功したとはいえ、チュートリアルに従っていただけなので細かい挙動などは把握できていないので、ダニング＝クルーガー曲線の「完全に理解した」フェーズにすら到達できていません。
これからもうちょっと試行錯誤を重ねて「AWS Amplifyチョットワカル」になっていきたいですね