AWS Amplify Gen2 ✖️ Nx(monorepo)✖️Next.js デプロイとサンドボックス作成、REST API(Lambda)まで

AWS AmplifyGen2がGAされたので、Nxのmonorepo構成で作成したNext.jsプロジェクトを、バックエンドとフロントエンドで別個のAmplifyアプリとして分離して、デプロイしてみます。

# Amplify BoostUp

[Amplify BoostUp #6](https://aws-amplify-jp.connpass.com/event/321534/)で発表しました。

https://speakerdeck.com/mah77ss/amplifygen2-plus-nxdegou-zhu-surunext-dot-jsmonorepogou-cheng

# 茨の道

下記Gen2のドキュメントを参考に進めました。

https://docs.amplify.aws/nextjs/deploy-and-host/fullstack-branching/mono-and-multi-repos/

https://docs.amplify.aws/nextjs/deploy-and-host/fullstack-branching/monorepos/

記載されている内容が少なく、デプロイエラーで詰まると下記のドキュメントなどと併せて読む必要があり、全体像の理解に時間がかかりました。

https://docs.aws.amazon.com/amplify/latest/userguide/monorepo-configuration.html

Gen2のドキュメントには「バックエンドのみのコードをデプロイできる」とあるのですが、手順に従って進めてもリポジトリの構成を自動検出してくれない(自動検出結果がおかしい)、自動作成されるamplify.ymlのままだとデプロイエラーが出るなど、リポジトリのコードがおかしいのか、Amplifyのビルド＆デプロイロジックに問題があるのか良く分からず、だいぶ混乱しました。

ここでは無事にフロント＆バックエンドともにデプロイできた手順を、Nxのワークスペース作成から進めていきます。

なお、Node.jsはバージョン18(node:18-bullseye-slim)、パッケージ管理はnpmを使用しており、パッケージのバージョンは記事執筆時点(2024/05/10)での最新をインストールしています。

Node.jsのバージョン2x系ではAmplifyのサンドボックス作成時にエラーが出ることが報告[^1]されており、ここでもバージョン18を使用しています。

Nx CLIのバージョンは18.3.4です。

:bulb: 数日前にNx 19.0がランチしてました。
https://www.youtube.com/watch?v=U6eO8-w9DR0

[^1]: [詰まった所②: サンドボックス環境が作成出来ない...](https://qiita.com/asuka0708japan/items/4a6d757a34b3fd127799#%E8%A9%B0%E3%81%BE%E3%81%A3%E3%81%9F%E6%89%80-%E3%82%B5%E3%83%B3%E3%83%89%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E7%92%B0%E5%A2%83%E3%81%8C%E4%BD%9C%E6%88%90%E5%87%BA%E6%9D%A5%E3%81%AA%E3%81%84)

# 使用する名前

test-nx : GitHubリポジトリ名 & Nxワークスペース名 & Next.js アプリ名
　Nxワークスペースのルートディレクトリ -> /workspace/services
　Next.js アプリのルートディレクトリ -> /workspace/services/apps/test-nx
test-shared-backend : Amplifyシェアード(共有)バックエンド
　バックエンドのルートディレクトリ -> /workspace/services/lib/test-shared-backend
test-nx-app : フロントアプリのAmplifyアプリ名
test-nx-shared-backend : シェアードバックエンドのAmplifyアプリ名

# 開発環境

開発用ローカルのコマンド実行はLinux(WSL2 Ubuntu上で動くDockerコンテナ)で実行しています。
シェルはzshで、あらかじめ、aws cliとnxがインストール済です。

エディタはVSCode/Cursor両方を使っています。
~~本当はCursor一本で行きたいのですが、CursorだとSSH経由のdevcontainer
を立ち上げた時に接続がすぐに切れてしまうので、VSCodeでdevcontainerを立ち上げて、CursorからもSSHでこのコンテナに接続して、GPTを使ったコーディングやデバッグ(Docker拡張機能からターミナルにも接続できるので)を行なっています。~~
:bulb: 2024/06/18 今日試したら、CursorからSSHでdevcontainer起動できるようになっていました。

```bash
# AWS CLIをインストール
$ apt-get update && apt-get install -y \
    curl \
    unzip \
    && curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
    && unzip awscliv2.zip \
    && ./aws/install \
    && rm -rf awscliv2.zip ./aws

# NXをインストール
$ npm i -g nx@latest
```

# AWS SSO

:::note info
AmplifyのCLIコマンド ampx 〜をAWS SSO認証で実行する場合、コマンドオプション --profile を指定します。(aws configure sso コマンドでSSO設定済前提で)
(例) npx ampx generate outputs --branch shared-main --app-id d10c0qxxx... --profile sso...
:::

# Nxワークスペース作成

空のtest-nxリポジトリを新規作成＆クローンして、Nxワークスペースを新規作成します。
(下記の例では --preset=next としていますが、後述する問題が出てくるため、 --preset=ts とするのが良いです。この場合手順が変わり、[ワークスペースと別にアプリを作る必要があります](https://qiita.com/mah77ss/items/816de5cc084c6f739fe7#nx%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E4%BD%9C%E6%88%90%E6%99%82%E3%81%AE%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E9%81%B8%E6%8A%9E))。
下記では--preset=nextとした場合の手順を示しています。

```bash
$ npx create-nx-workspace@latest --preset=next

NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Where would you like to create your workspace? · org
✔ Application name · test-nx
✔ Would you like to use the App Router (recommended)? · Yes
✔ Would you like to use the src/ directory? · Yes
✔ Test runner to use for end to end (E2E) tests · playwright
✔ Default stylesheet format · tailwind
✔ Set up CI with caching, distribution and test deflaking · skip
✔ Would you like remote caching to make your build faster? · skip

 NX   Creating your v19.0.2 workspace.

✔ Installing dependencies with npm
✔ Successfully created the workspace: org.

 NX   Directory is already under version control. Skipping initialization of git.


—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


 NX   First time using Nx? Check out this interactive Nx tutorial.

https://nx.dev/react-tutorial/1-code-generation


```

リポジトリルート/orgにNxワークスペースが作成されるので、ワークスペースのファイルをリポジトリルートへ移動します。

```bash
# 隠しファイルを含めて、カレントディレクトリへ全て移動
# zshを使用しているので、bashでは動作しない。
$ rm -rf .nx # Nxキャッシュディレクトリをまず削除
$ mv org/*(D) .
$ rmdir org
```

:warning: リポジトリルートのサブディレクトリにNxワークスペースを配置した場合、Amplifyのプロジェクト自動検出が動作しない。

:bulb: npx create-nx-workspace..でGitリポジトリも初期化してくれるようなので、先にワークスペースを作ってからGitHubにpushするのがいいかも知れない。

ワークスペースの作成が完了後、コミットしておきます。

```bash
$ git add .
$ git commit -m "Initial commit"
```

# Amplifyシェアードバックエンドの作成

NxでJavaScript用テンプレートを自動作成します。
ここではライブラリ用のディレクトリに lib を指定しています。
(AmplifyのMonorepo setupドキュメントでは packages にあたる部分)

```bash
$ nx g @nx/js:library test-shared-backend --directory lib

NX  Generating @nx/js:library

✔ Which unit test runner would you like to use? · none
✔ Which bundler would you like to use to build the library? Choose 'none' to skip build setup. · tsc
✔ What should be the project name and where should it be generated? · lib--test-shared-backend @ lib/test-shared-backend
In Nx 20, generating projects will no longer derive the name and root.
Please provide the exact project name and root in the future.
Example: nx g @nx/js:library lib--test-shared-backend --directory lib/test-shared-backend
CREATE lib/test-shared-backend/tsconfig.json
CREATE lib/test-shared-backend/src/index.ts
CREATE lib/test-shared-backend/src/lib/lib--test-shared-backend.ts
CREATE lib/test-shared-backend/tsconfig.lib.json
CREATE lib/test-shared-backend/README.md
CREATE lib/test-shared-backend/package.json
UPDATE nx.json
CREATE lib/test-shared-backend/project.json
CREATE lib/test-shared-backend/.eslintrc.json
UPDATE tsconfig.base.json

 NX   👀 View Details of lib--test-shared-backend

Run "nx show project lib--test-shared-backend --web" to view details about this project.
```

完了すると、リポジトリルート/lib/test-shared-backendが作成され、中にpackage.jsonなど作成されています。

![スクリーンショット 2024-05-10 22.55.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/5f012c6a-d9ec-0a07-890e-42f4b62eff55.png)

続いて、作成されたディレクトリの中でamplifyをインストールします。

```bash
$ cd lib/test-shared-backend/
$ npm create amplify@latest
? Where should we create your project? .

Installing devDependencies:
 - @aws-amplify/backend
 - @aws-amplify/backend-cli
 - aws-cdk@^2
 - aws-cdk-lib@^2
 - constructs@^10.0.0
 - typescript@^5.0.0
 - tsx
 - esbuild

Installing dependencies:
 - aws-amplify

✔ DevDependencies installed
✔ Dependencies installed
✔ Template files created
Successfully created a new project!

Welcome to AWS Amplify!
 - Get started by running npx ampx sandbox.
 - Run npx ampx help for a list of available commands.

Amplify Gen 2 collects anonymous telemetry data about general usage of the CLI. Participation is optional, and you may opt-out by using npx ampx configure telemetry disable. To learn more about telemetry, visit https://docs.amplify.aws/gen2/reference/telemetry
```

ここで一度コミットしておきます。

```bash
$ cd ../../
$ git add .
$ git commit -m "Add amplify"
```

Nxで管理する場合、package.jsonがリポジトリルート直下の1つのみになるので、test-shared-backend直下のpackage.jsonのdependenciesとdevDependenciesを、リポジトリルートのpackage.jsonへ手動マージします。

マージ後のpackage.json↓
```json
{
  "name": "@org/source",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {},
  "private": true,
  "dependencies": {
    "next": "14.0.4",
    "react": "18.3.1",
    "react-dom": "18.3.1",
    "aws-amplify": "^6.3.0",
    "tslib": "^2.3.0"
  },
  "devDependencies": {
    "@nx/devkit": "19.0.2",
    "@nx/eslint": "19.0.2",
    "@nx/eslint-plugin": "19.0.2",
    "@nx/jest": "19.0.2",
    "@nx/js": "19.0.2",
    "@nx/next": "19.0.2",
    "@nx/playwright": "19.0.2",
    "@nx/workspace": "19.0.2",
    "@playwright/test": "^1.36.0",
    "@swc-node/register": "~1.8.0",
    "@swc/core": "~1.3.85",
    "@swc/helpers": "~0.5.2",
    "@types/jest": "^29.4.0",
    "@types/node": "18.16.9",
    "@types/react": "18.3.1",
    "@types/react-dom": "18.3.0",
    "@typescript-eslint/eslint-plugin": "^7.3.0",
    "@typescript-eslint/parser": "^7.3.0",
    "autoprefixer": "10.4.13",
    "babel-jest": "^29.4.1",
    "eslint": "~8.57.0",
    "eslint-config-next": "14.0.4",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-import": "2.27.5",
    "eslint-plugin-jsx-a11y": "6.7.1",
    "eslint-plugin-playwright": "^0.15.3",
    "eslint-plugin-react": "7.32.2",
    "eslint-plugin-react-hooks": "4.6.0",
    "jest": "^29.4.1",
    "jest-environment-jsdom": "^29.4.1",
    "nx": "19.0.2",
    "postcss": "8.4.21",
    "prettier": "^2.6.2",
    "tailwindcss": "3.2.7",
    "ts-jest": "^29.1.0",
    "ts-node": "10.9.1",
    "@aws-amplify/backend": "^1.0.1",
    "@aws-amplify/backend-cli": "^1.0.2",
    "aws-cdk": "^2.141.0",
    "aws-cdk-lib": "^2.141.0",
    "constructs": "^10.3.0",
    "esbuild": "^0.21.1",
    "tsx": "^4.9.3",
    "typescript": "^5.4.5"
  }
}
```

:bulb: Nx初心者なので、他にいい手順があれば教えてほしいです。

マージ後、test-shared-backend直下のpackage.json、package-lock.json、node_modulesディレクトリを削除します。

test-shared-backend/amplify直下にもpackage.jsonがあり、{ "type": "module" }のみ書かれています。これも、nxのコマンドを使った時に下記エラーが出る原因になるため、削除します。

```bash
Failed to process project graph. Run "nx reset" to fix this. Please report the issue if you keep seeing it. See errors below.
```

削除後のディレクトリ↓

![スクリーンショット 2024-05-11 20.01.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/52153a66-a55f-b66c-ac5d-1705d99d1ff7.png)

npm clean installして、問題が出ないことを確認します。

```bash
$ npm ci
```

問題なければコミットします。

```bash
$ git add .
$ git commit -m "Merge packages"
```

# Amplifyシェアードバックエンドのデプロイ

## Next.jsのパッケージ削除

ここで小細工をします。

事前準備として、package.jsonのdependenciesからNext.jsのパッケージを削除しておきます。

削除せずAWSコンソールからAmplifyアプリを作成した場合、[Next.js][Amplify Gen 2]リポジトリとして認識されてしまい、デプロイ時に下記のエラーが出て失敗します。

```text
[ERROR]: !!! CustomerError: Can't find required-server-files.json in build output directory
```

Next.jsとしてリポジトリが認識されてしまうと、ビルド結果のファイル「required-server-files.json」が必須となるようです。


削除して、コミット&プッシュしておきます。

```bash
$ git add .
$ git commit -m "Fix packages"
$ git push
```

### Nxワークスペース作成時のテンプレート選択

:bulb: Nxワークスペースを最初に作る時に、テンプレートをNext.jsではなくts(TypeScript)やnodeなどで作成しておくと、回避できる。

```bash
# ワークスペース作成
$ npx create-nx-workspace@latest --preset=ts

# バックエンドの作成
$ nx g @nx/node:library --name=test-shared-backend --directory=lib

# Nxのジェネレートで対応テンプレートが見つからない場合、インストールする。
$ npm i -D @nx/node # Node.js
$ npm i -D @nx/next # Next.js

# アプリの作成
$ nx g @n/next:app test-app --directory=apps
```

## Amplifyアプリの作成(バックエンド)

AWSコンソールからAmplifyを開き、GitHubのリポジトリと接続して、monorepoを有効にして、ルートディレクトリのパスを入力し、次へ進みます。

```text
モノレポルートディレクトリ→
lib/test-shared-backend
```
:warning:  /lib/..と、パスの先頭に / をつけても次へ進めますが、デプロイ時にエラーになるため注意

![スクリーンショット 2024-05-10 23.25.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/98faa663-1ac1-dc89-f61c-77744d1888b9.png)

自動検出されたフレームワークが「Amplify Gen 2」のみになっていれば成功です。
ここで「Next.js」など、フロントのフレームワークが表示されていた場合、前の手順に戻ってpackage.jsonの修正内容を確認します。

フロントエンドビルドコマンドは、特にビルドするものがないので、公式ドキュメント(Separate frontend and backend teams)に記載の通り、distディレクトリとindex.htmlを作成するに留めます。

```text
アプリケーションの名前->
test-nx-shared-backend
フロントエンドビルドコマンド->
mkdir ./dist && touch ./dist/index.html
出力ディレクトリをビルド->
dist

他、デフォルト値を使用
```

![スクリーンショット 2024-05-11 13.47.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/57609952-e441-ede4-86f7-aa87d9cbae41.png)

次へ進み、設定内容を確認します。

![スクリーンショット 2024-05-11 13.48.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/d3c4efd6-58cd-ab53-e7c1-bf0d8e1dc1ed.png)

問題なければ、保存してデプロイします。

:bulb: IAMサービスロールとポリシーが自動作成されます。Amplifyアプリを削除しただけではこのサービスロールとポリシーが削除されないため、アプリが不要になったら、手動の削除が必要です(自動作成した場合、タグをつけておくと管理しやすいか)。

~~余談ですが、Amplifyアプリ削除時に求められる「削除」の文字入力は、現時点では、日本語ではなく英語で「delete」と入力しないとアプリを削除できないです。~~ 修正されました。

デプロイ実行結果で、エラーが出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/2727ba1d-1195-a8a7-0920-3421747eca8a.png)

### エラー対処「CustomerError: Build path does not exist, please check the 'buildPath' value in BuildSpec」

Amplifyコンソールから本アプリのホスティング→ビルドの設定を開き、amplify.ymlをダウンロードして、lib/test-shared-backend直下へ配置します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/fde1b4bf-551b-b119-0fac-e90668893246.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/83193a51-9bad-e6d4-5c95-347283fe3a5c.png)

配置後、amplify.ymlの中身を下記のように書き換えます。

```yaml
version: 1
applications:
  - backend:
      phases:
        build:
          commands:
            - npm ci --cache .npm --prefer-offline
            - cd lib/test-shared-backend
            - 'npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID'
    frontend:
      phases:
        build:
          commands: ['mkdir ./dist && touch ./dist/index.html']
      artifacts:
        baseDirectory: lib/test-shared-backend/dist
        files:
          - '**/*'
      cache:
        paths:
          - '.npm/**/*'
      buildPath: .
    appRoot: lib/test-shared-backend
```

色々デフォルト状態から変更していますが、buildPath以外にも、エラーが色々出るので、最終的にデプロイできたamplify.ymlの中身がこのようになりました。

これが正しいのかは・・分からず、試行錯誤の結果です。

:bulb: 正しい方法があれば教えてほしいです。

更新したamplify.ymlをリポジトリへ上げます。

```bash
$ git add .
$ git commit -m "Fix amplify.yml"
$ git push
```

再デプロイが実行され、デプロイが成功することを確認します。

![スクリーンショット 2024-05-11 14.00.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/30c5f8a5-33bd-1cb1-1662-865be7b8d606.png)

## デプロイ後の確認

認証(Auth)とデータ(Data)のAWSリソースが作成されていることを確認します。

CDKをデフォルトから変えていないので、Authはメールアドレスによるユーザー作成、DataにはTodoテーブルが作成されています。

![スクリーンショット 2024-05-11 20.11.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/3bd621dc-73ee-0fab-7dfa-8e87829efb85.png)

![スクリーンショット 2024-05-11 20.11.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/6b8c9b3a-edec-2a59-a881-605e24944e5a.png)

今後、バックエンドの構成を変更するときは、lib/test-shared-backend/amplify以下のCDKを修正します。

# Next.jsフロントエンドのデプロイ

## Next.jsのパッケージ復元

package.jsonのdependenciesに、削除したNext.jsパッケージを追加して、元に戻します。

```json
  "dependencies": {
    "next": "14.0.4",
    "react": "18.3.1",
    "react-dom": "18.3.1",
    "aws-amplify": "^6.3.0",
    "tslib": "^2.3.0"
  },
```
```bash
$ npm i
```

## Next.jsのローカル起動

最初にNxテンプレートを作成したタイミングで、リポジトリルート/apps/test-nxにNext.jsアプリが作成されていると思います。

下記コマンドをワークスペースのルートで実行することで、Next.jsアプリをローカル実行できますが、test-nx/project.jsonにビルド用のコンフィグがないのでエラーになります。

```bash
$ nx serve test-nx

NX   Failed to process project graph. Run "nx reset" to fix this. Please report the issue if you keep seeing it.

Pass --verbose to see the stacktraces.
```

project.jsonにNext.jsビルド用のターゲットを追加して、保存します。
(内容は、下記のNxレシピを参考に、targetsへとりあえず貼り付けています)

https://github.com/nrwl/nx-recipes/tree/main/nextjs-trpc

```json
{
  "name": "test-nx",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/test-nx",
  "projectType": "application",
  "tags": [],
  "// targets": "to see all targets run: nx show project test-nx --web",
  "targets": {
    "build": {
      "executor": "@nx/next:build",
      "outputs": ["{options.outputPath}"],
      "defaultConfiguration": "production",
      "options": {
        "outputPath": "dist/apps/test-nx"
      },
      "configurations": {
        "development": {
          "outputPath": "apps/test-nx"
        },
        "production": {}
      }
    },
    "serve": {
      "executor": "@nx/next:server",
      "defaultConfiguration": "development",
      "options": {
        "buildTarget": "test-nx:build",
        "dev": true
      },
      "configurations": {
        "development": {
          "buildTarget": "test-nx:build:development",
          "dev": true
        },
        "production": {
          "buildTarget": "test-nx:build:production",
          "dev": false
        }
      }
    },
    "export": {
      "executor": "@nx/next:export",
      "options": {
        "buildTarget": "test-nx:build:production"
      }
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
      "options": {
        "jestConfig": "apps/test-nx/jest.config.ts"
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint",
      "outputs": ["{options.outputFile}"],
      "options": {
        "lintFilePatterns": ["apps/test-nx/**/*.{ts,tsx,js,jsx}"]
      }
    }
  }
}
```

再度、serveを実行します。

```bash
$ nx serve test-nx

> nx run test-nx:serve:development

   ▲ Next.js 14.0.4
   - Local:        http://localhost:4200


   We detected TypeScript in your project and reconfigured your tsconfig.json file for you. Strict-mode is set to false by default.
   The following suggested values were added to your tsconfig.json. These values can be changed to fit your project's needs:

        - include was updated to add '.next/types/**/*.ts'

 ✓ Ready in 2.1s
 ○ Compiling / ...
 ✓ Compiled / in 3.1s (427 modules)
 ✓ Compiled in 248ms (216 modules)

```

http://localhost:4200 へアクセスして、テンプレートページが表示されることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/e630485e-13cc-0552-d602-9d795479453b.png)

最新のコードを上げておきます。

```bash
$ git add .
$ git commit -m "Confirm front serve"
$ git push
```

## Amplifyアプリの作成(フロントエンド)

AWSコンソールからAmplifyを開き、GitHubのリポジトリと接続して、monorepoを有効にして、フロントのルートディレクトリのパスを入力し、次へ進みます。

```text
モノレポルートディレクトリ→
apps/test-nx
```

![スクリーンショット 2024-05-11 14.51.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/1286460c-2679-b534-68eb-b3a2e6a03f2c.png)

自動検出されたフレームワークが「Next.js」「Amplify Gen 2」のみになっていればOKです。アプリケーションの名前を変更します。

```text
アプリケーションの名前->
test-nx-app

他、デフォルト値を使用
```

![スクリーンショット 2024-05-11 14.52.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/ab9a5a86-4cff-835d-884a-ca071a918db8.png)

次へ進み、設定内容を確認します。

![スクリーンショット 2024-05-11 14.55.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/a03656e2-0e0d-7337-c916-6511557d1f76.png)

問題なければ、保存してデプロイします。

デプロイ実行結果で、エラーが出ます。

![スクリーンショット 2024-05-11 15.03.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/0a985b44-beb2-dbb6-e449-e43a7c582385.png)

### エラー対処「CustomerError: Build path does not exist, please check the 'buildPath' value in BuildSpec」

バックエンドと同じく、フロントエンドでもbuildPathのエラーが出ます。
前回と同じように、Amplifyコンソールから本アプリのホスティング→ビルドの設定を開き、amplify.ymlをダウンロードして、apps/test-nx直下へ配置します。

![スクリーンショット 2024-05-11 15.06.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/cd7a7770-815f-7e7d-53c6-561166d2075e.png)

![スクリーンショット 2024-05-11 19.23.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/280a1067-35c5-0e0a-5a21-cda0df5b5fd4.png)

配置後、amplify.ymlの中身を下記のように書き換えます。

```yaml
version: 1
applications:
  - backend:
      phases:
        build:
          commands:
            - npm ci --cache .npm --prefer-offline
            # - npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
            - npx ampx generate outputs --branch $AWS_BRANCH --app-id BACKEND-APP-ID
            # バックエンド接続情報のファイルコピー。generate を実行する場所間違えているかな・・。
            - cp ./amplify_outputs.json ./apps/test-nx
    frontend:
      phases:
        preBuild:
          commands:
            - mkdir -p dist/apps/test-nx
        build:
          commands:
            - npx nx build test-nx
      artifacts:
        baseDirectory: dist/apps/test-nx/.next
        files:
          - '**/*'
      cache:
        paths:
          - .next/cache/**/*
          - .npm/**/*
      buildPath: .
    appRoot: apps/test-nx
```

buildPathなどパス系のエラー回避に加えて、公式ドキュメントに記載がある、フロントからバックエンドへ接続するためのコマンドも、backend: build: commands: へ加えておきます。

この時、BACKEND-APP-ID を、すでにデプロイ済のバックエンドアプリのAppIDと置換します。(画像中、d10c0q〜 から始まる部分が、バックエンドアプリのAppIDです)

![スクリーンショット 2024-05-11 19.31.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/facec5aa-66f1-c5ac-5e78-96816295dc3c.png)

もともと記載があった npx ampx pipeline-deploy の行はコメントアウトして、無効化しています。

```yaml
applications:
  - backend:
      phases:
        build:
          commands:
            - npm ci --cache .npm --prefer-offline
            # - npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
            - npx ampx generate outputs --branch $AWS_BRANCH --app-id d10c0qxxx...
```

このコマンドの編集を行わないと、下記のエラーが出てビルドが通らないです。

```bash
FileConventionError: Amplify Backend not found in /codebuild/output/src1934378443/src/test-nx.
```

更新したamplify.ymlをリポジトリへ上げます。

```bash
$ git add .
$ git commit -m "Fix amplify.yml"
$ git push
```

再デプロイが実行され、デプロイが成功することを確認します。

![スクリーンショット 2024-05-11 19.36.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/5170627f-5ea3-927c-d6e4-601eeed7313c.png)

## デプロイ後の確認

デプロイ成功後、ドメインのURLを参照し、無事にNextJSのフロント画面が表示されることを確認します。

![スクリーンショット 2024-05-11 19.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/ee6c2eb6-b148-fbd4-84cc-9f138c468843.png)

# デプロイブランチ

今回は main ブランチのみ使用したため、main ブランチをプッシュするとバックエンド、フロントともに再デプロイ処理が走ります。

main ブランチからバックエンドとフロントでブランチを分けて、それぞれ個別にAmplifyアプリに紐づけておくと別々にデプロイが可能ですが、モノレポ構成の場合、一般的には一緒にデプロイを走らせるようです。

:warning: バックエンドをshared-mainブランチ、フロントをfront-mainブランチなど異なるブランチ名を使用する場合、フロント側のビルド設定`npx ampx generate outputs --branch $AWS_BRANCH --app-id BACKEND-APP-ID`の $AWS_BRANCH を、対応するバックエンドのブランチ名(shared-mainなど)へ変更が必要です。

Amplifyアプリへ紐付けるブランチは、AWSコンソールから簡単に追加/削除でき、本番稼働ブランチの変更もできます。

:warning: Basic認証などアクセス制御はブランチごとのため、設定している場合、ブランチを追加したら忘れずに設定しておきます。

![スクリーンショット 2024-05-19 13.29.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/a559e079-6c22-8f0c-94ff-2c48886a8baa.png)

後述するサンドボックを使った開発では、front-mainブランチでフロントおよびバックエンドの開発を行い、リリースするタイミングで main ブランチと shared-main へマージ(プルリク)し、Amplify 側のデプロイプロセスを動作させています。

この形だと front-main から develop や feature ブランチを作成する形になりそうです。(どういう形が一番良いのか、手探り中です)

# サンドボックスの作成

バックエンドのディレクトリ(lib/test-shared-backend)へ移動して、下記コマンドを実行して、package.jsonとAmplifyサンドボックスを作成します。

```bash
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (test-shared-backend) amplify-backend
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /workspace/services/lib/test-shared-backend/package.json:

{
  "name": "amplify-backend",
  "version": "1.0.0",
  "description": "This library was generated with [Nx](https://nx.dev).",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) 
```

続いて、ampxコマンドの実行です。

```bash
$ npx ampx sandbox --outputs-out-dir ../../apps/test-nx
  
  Amplify Sandbox
  
  Identifier:   node
  Stack:        amplify-amplifybackend-node-sandbox-1ad730e7d3
  
  To specify a different sandbox identifier, use --identifier


✨  Synthesis time: 0.49s

⚠️ The --hotswap and --hotswap-fallback flags deliberately introduce CloudFormation drift to speed up deployments
⚠️ They should only be used for development - never use them for your production Stacks!

amplify-amplifybackend-node-sandbox-1ad730e7d3:  start: Building 4adc3952616d06259c60d1953b9de7868b262e9618cbccd819760c1c2a83c8fe:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  success: Built 4adc3952616d06259c60d1953b9de7868b262e9618cbccd819760c1c2a83c8fe:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  start: Building f6bf6a2c43df9880a1e61f0771a573d91474f7a5b5e6210b55da505511dca2e2:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  success: Built f6bf6a2c43df9880a1e61f0771a573d91474f7a5b5e6210b55da505511dca2e2:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  start: Publishing 4adc3952616d06259c60d1953b9de7868b262e9618cbccd819760c1c2a83c8fe:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  start: Publishing f6bf6a2c43df9880a1e61f0771a573d91474f7a5b5e6210b55da505511dca2e2:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  success: Published 4adc3952616d06259c60d1953b9de7868b262e9618cbccd819760c1c2a83c8fe:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3:  success: Published f6bf6a2c43df9880a1e61f0771a573d91474f7a5b5e6210b55da505511dca2e2:current_account-current_region
amplify-amplifybackend-node-sandbox-1ad730e7d3: deploying... [1/1]

⚠️ The following non-hotswappable changes were found:
    logicalID: deploymentType, type: Stack Output, reason: output was changed
    logicalID: region, type: Stack Output, reason: output was changed
    logicalID: userPoolId, type: Stack Output, reason: output was changed
    logicalID: webClientId, type: Stack Output, reason: output was changed
    logicalID: identityPoolId, type: Stack Output, reason: output was changed
    logicalID: authRegion, type: Stack Output, reason: output was changed
    logicalID: allowUnauthenticatedIdentities, type: Stack Output, reason: output was changed
    logicalID: signupAttributes, type: Stack Output, reason: output was changed
    logicalID: usernameAttributes, type: Stack Output, reason: output was changed
    logicalID: verificationMechanisms, type: Stack Output, reason: output was changed
    logicalID: passwordPolicyMinLength, type: Stack Output, reason: output was changed
    logicalID: passwordPolicyRequirements, type: Stack Output, reason: output was changed
    logicalID: awsAppsyncApiId, type: Stack Output, reason: output was changed
    logicalID: awsAppsyncApiEndpoint, type: Stack Output, reason: output was changed
    logicalID: awsAppsyncAuthenticationType, type: Stack Output, reason: output was changed
    logicalID: awsAppsyncRegion, type: Stack Output, reason: output was changed
    logicalID: amplifyApiModelSchemaS3Uri, type: Stack Output, reason: output was changed
    logicalID: awsAppsyncAdditionalAuthenticationTypes, type: Stack Output, reason: output was changed
    logicalID: auth179371D7, type: AWS::CloudFormation::Stack, reason: resource 'auth179371D7' was created by this deployment
    logicalID: data7552DF31, type: AWS::CloudFormation::Stack, reason: resource 'data7552DF31' was created by this deployment
    logicalID: CDKMetadata, type: AWS::CDK::Metadata, reason: resource 'CDKMetadata' was created by this deployment

Could not perform a hotswap deployment, as the stack amplify-amplifybackend-node-sandbox-1ad730e7d3 contains non-Asset changes
Falling back to doing a full deployment
amplify-amplifybackend-node-sandbox-1ad730e7d3: creating stack...
amplify-amplifybackend-node-sandbox-1ad730e7d3-auth179371D7-124WX4RQYOKMO |   0 | 5:12:49 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | amplify-amplifybackend-node-sandbox-1ad730e7d3-auth179371D7-124WX4RQYOKMO User Initiated
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   0 | 5:12:45 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | amplify-amplifybackend-node-sandbox-1ad730e7d3 User Initiated
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   0 | 5:12:49 AM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata         | auth/CDKMetadata/Default (CDKMetadata) 
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   0 | 5:12:49 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | auth.NestedStack/auth.NestedStackResource (auth179371D7) 
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   0 | 5:12:49 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | auth.NestedStack/auth.NestedStackResource (auth179371D7) Resource creation Initiated
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   0 | 5:12:50 AM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata         | auth/CDKMetadata/Default (CDKMetadata) Resource creation Initiated
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   1 | 5:12:50 AM | CREATE_COMPLETE      | AWS::CDK::Metadata         | auth/CDKMetadata/Default (CDKMetadata) 
  1 Currently in progress: amplify-amplifybackend-node-sandbox-1ad730e7d3-auth179371D7-124WX4RQYOKMO, amplify-amplifybackend-node-sandbox-1ad730e7d3, auth179371D7
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   2 | 5:13:36 AM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | auth.NestedStack/auth.NestedStackResource (auth179371D7) 
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   2 | 5:13:37 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | data.NestedStack/data.NestedStackResource (data7552DF31) 
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:37 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G User Initiated
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:42 AM | CREATE_IN_PROGRESS   | AWS::IAM::Role             | data/Custom::S3AutoDeleteObjectsCustomResourceProvider/Role (CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092) 
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:42 AM | CREATE_IN_PROGRESS   | AWS::AppSync::GraphQLApi   | data/amplifyData/GraphQLAPI (amplifyDataGraphQLAPI42A6FA33) 
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:42 AM | CREATE_IN_PROGRESS   | AWS::CDK::Metadata         | auth/CDKMetadata/Default (CDKMetadata) 
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:42 AM | CREATE_IN_PROGRESS   | AWS::Lambda::LayerVersion  | data/amplifyData/AmplifyCodegenAssets/AmplifyCodegenAssetsDeployment/AwsCliLayer (amplifyDataAmplifyCodegenAssetsAmplifyCodegenAssetsDeploymentAwsCliLayerE322F905) 
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:42 AM | CREATE_IN_PROGRESS   | AWS::S3::Bucket            | data/amplifyData/AmplifyCodegenAssets/AmplifyCodegenAssetsBucket (amplifyDataAmplifyCodegenAssetsAmplifyCodegenAssetsBucket9CCB4ACA) 
amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G |   2 | 5:13:42 AM | CREATE_IN_PROGRESS   | AWS::IAM::Role             | data/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C1536MiB/ServiceRole (CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C1536MiBServiceRoleA41FC8C2) 
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   2 | 5:13:38 AM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | data.NestedStack/data.NestedStackResource (data7552DF31) Resource creation Initiated
  2 Currently in progress: amplify-amplifybackend-node-sandbox-1ad730e7d3-auth179371D7-124WX4RQYOKMO, amplify-amplifybackend-node-sandbox-1ad730e7d3, data7552DF31, amplify-amplifybackend-node-sandbox-1ad730e7d3-data7552DF31-2V55EXWEYM7G, CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092, amplifyDataGraphQLAPI42A6FA33, CDKMetadata, amplifyDataAmplifyCodegenAssetsAmplifyCodegenAssetsDeploymentAwsCliLayerE322F905, amplifyDataAmplifyCodegenAssetsAmplifyCodegenAssetsBucket9CCB4ACA, CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C1536MiBServiceRoleA41FC8C2
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   3 | 5:16:15 AM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | data.NestedStack/data.NestedStackResource (data7552DF31) 
amplify-amplifybackend-node-sandbox-1ad730e7d3 |   4 | 5:16:16 AM | CREATE_COMPLETE      | AWS::CloudFormation::Stack | amplify-amplifybackend-node-sandbox-1ad730e7d3 

 ✅  amplify-amplifybackend-node-sandbox-1ad730e7d3

✨  Deployment time: 212.53s

Outputs:
amplify-amplifybackend-node-sandbox-1ad730e7d3.allowUnauthenticatedIdentities = true
amplify-amplifybackend-node-sandbox-1ad730e7d3.amplifyApiModelSchemaS3Uri = s3://amplify-amplifybackend-no-amplifydataamplifycodege-yzgdz5sruxai/model-schema.graphql
amplify-amplifybackend-node-sandbox-1ad730e7d3.authRegion = ap-northeast-1
amplify-amplifybackend-node-sandbox-1ad730e7d3.awsAppsyncAdditionalAuthenticationTypes = AMAZON_COGNITO_USER_POOLS
amplify-amplifybackend-node-sandbox-1ad730e7d3.awsAppsyncApiEndpoint = https://k5zwi573yjckdgmgd5km4zunzy.appsync-api.ap-northeast-1.amazonaws.com/graphql
amplify-amplifybackend-node-sandbox-1ad730e7d3.awsAppsyncApiId = bjceldto2zgnlntet6yqoyfjle
amplify-amplifybackend-node-sandbox-1ad730e7d3.awsAppsyncAuthenticationType = AWS_IAM
amplify-amplifybackend-node-sandbox-1ad730e7d3.awsAppsyncRegion = ap-northeast-1
amplify-amplifybackend-node-sandbox-1ad730e7d3.deploymentType = sandbox
amplify-amplifybackend-node-sandbox-1ad730e7d3.identityPoolId = ap-northeast-1:f88e12e0-8ac8-4697-9472-4d841ef93269
amplify-amplifybackend-node-sandbox-1ad730e7d3.passwordPolicyMinLength = 8
amplify-amplifybackend-node-sandbox-1ad730e7d3.passwordPolicyRequirements = ["REQUIRES_NUMBERS","REQUIRES_LOWERCASE","REQUIRES_UPPERCASE","REQUIRES_SYMBOLS"]
amplify-amplifybackend-node-sandbox-1ad730e7d3.region = ap-northeast-1
amplify-amplifybackend-node-sandbox-1ad730e7d3.signupAttributes = ["email"]
amplify-amplifybackend-node-sandbox-1ad730e7d3.userPoolId = ap-northeast-1_hCuBi9Ztx
amplify-amplifybackend-node-sandbox-1ad730e7d3.usernameAttributes = ["email"]
amplify-amplifybackend-node-sandbox-1ad730e7d3.verificationMechanisms = ["email"]
amplify-amplifybackend-node-sandbox-1ad730e7d3.webClientId = 69r5bnrsipc6gujkn74u6mfq2q
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:637423527734:stack/amplify-amplifybackend-node-sandbox-1ad730e7d3/6d75afa0-159e-11ef-b373-06ae7934c5cb

✨  Total time: 213.02s


[Sandbox] Watching for file changes...
File written: ../../apps/test-nx/amplify_outputs.json
```

これで、フロントのNext.jsアプリのディレクトリに amplify_outputs.json (旧amplifyconfiguration.json[^2])が作成され、バックエンドへの接続情報が出力されます。

[^2]: [Amplify Gen2がGAとなり、CLIのコマンドが変更になりました](https://dev.classmethod.jp/articles/amplify-gen2-ga-cli/)

:warning: amplify_outputs.json 出力後、このファイルが誤ってホスティングのコードや他の開発者のコードに紛れ込まないように、.gitignore へ追加しておきます。

```text
# Amplify
amplify_outputs.json
```

AWSコンソールからバックエンドのサンドボックスを確認すると、無事にサンドボックスが1つ作成されていることが確認できます。

![スクリーンショット 2024-05-19 14.27.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/e0c51e2c-d50c-d2ab-9609-70504764c9a0.png)

Nxワークスペースの場合、package.json はワークスペースルートにただ一つ配置するはずなのですが、ワークスペースルートで`npx ampx sandbox --dir-to-watch lib/test-shared-backend --outputs-out-dir apps/test-nx`を実行した場合、監視ディレクトリにバックエンドのディレクトリを指定していても、`Amplify Backend not found in /workspace/services.`とエラーが発生するため、バックエンドのルートで`npx ampx sandbox`するために`npm init`して package.json を作成しています。

:warning: このpackage.jsonをNxワークスペースに含めても、今のところ問題は出てないです。

この状態で、ワークスペースルートで`nx serve test-nx`を実行すると、Next.jsアプリが起動し、Authなどサンドボックスのバックエンドと接続した状態で、アプリケーションが動きます。

Next.jsアプリでAuthなど認証系のコンポーネントを実装する場合、公式マニュアルもありますが、下記のYouTube動画なども参考になります。(GA前の録画なので、amplifyconfiguration.json など古い名称が使われていますが)

<iframe width="560" height="315" src="https://www.youtube.com/embed/wcSMnICY-_8?si=APS4xNSNL13c3Dfq" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

:bulb: 動画の1時間25分すぎに、AmplifyUIライブラリのボタンが表示されない問題(AmplifyUIとTailwind CSSを同時に使用している場合に発生する問題)の修正方法(global.cssの修正)も示されているので、もしお困りの方がおられれば確認してみるといいかもです。

# ホスティングバックエンドへの接続

サンドボックスではなく、本番環境など直接ホスティングのバックエンドへ接続したい場合、`npx ampx generate`コマンドをフロントエンドのルートディレクトリで実行すると、ホスティングへの接続情報を記した 
amplify_outputs.json が出力されるため、(たぶん)ホスティングのバックエンドへアプリを接続できると思います。(検証予定)

```bash
# (例) 環境変数 $AWS_BRANCH  は、実際のバックエンドブランチの値へ変更しておく。
$ npx ampx generate outputs --branch shared-main --app-id d10c0qxxx...
```

# 開発余談

## useEffectが発火されない？

エディタのターミナル(VSCode/Cursor)には`load component`のconsole.logしか出力されなかっため、useEffect自体が発火されていないと勘違いして、時間を浪費しました。

結論を言えば、ブラウザのデバッガのコンソールログには`load component`と`useEffect called`両方が出力されていたので、useEffect自体は発火されており、エディタのターミナルにuseEffectだけ表示されていないだけでした。

:question: てっきり、片方のconsole.logが出るなら、もう片方のconsole.logも同じターミナルにも出ると思っていたので・・・？

```page.tsx
'use client';
import { useEffect } from 'react';

export default function TestPage() {
  console.log('load component');
  useEffect(() => {
    console.log('useEffect called');
  }, []);

  return (
    <div>
      ...
    </div>
  );
}

```

## npm ERR! signal SIGBUS

ある時から急にバックエンドのデプロイに失敗するようになり、下記エラーが表示され出した。

```bash
npm ERR! path /codebuild/output/src4175144631/src/psynius/node_modules/@nx/node/node_modules/nx
npm ERR! command failed
npm ERR! signal SIGBUS
npm ERR! command sh -c node ./bin/post-install
npm ERR! A complete log of this run can be found in: /codebuild/output/src4175144631/src/psynius/.npm/_logs/2024-06-03T14_09_39_905Z-debug-0.log
!!! Build failed
[ERROR]: !!! Error: Command failed with exit code 1
```

フロントの package.json と同じなのに、フロント側のデプロイは成功します。

SIGBUS、バスエラーって・・・・。

原因不明なので、とりあえず package.json のパッケージを最新化して、`npm update` そして再デプロイします。

```bash
 # npm-check-updatesをインストール
 > sudo npm install -g npm-check-updates
 
 # 最新パッケージの確認
 # グローバルインストールではない場合、npx コマンドの先頭に付与する。
 > ncu
 > ncu -u # package.json の更新
 > npm update
```

eslint の 9.x 系は `npm update` 時に依存関係のエラーが出るため、package.json の eslint のバージョンを手動で修正します。

```
"eslint": "~8.57.0",
```

これで再度 `npm update` を実行し、Amplify へ再デプロイしたら、無事にデプロイ成功するようになりました。

## Cognitoの属性を更新できない？ユーザープールの再作成

Cognitoのユーザープールですでに作成済の属性を変更しようとした時など、バックエンドの更新に失敗する場合があります。

```
❌ [1mamplify-xxx-sharedmain-branch-xxx[22m failed: Error: The stack named amplify-xxx-sharedmain-branch-xxx failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Invalid AttributeDataType input, consider using the provided AttributeDataType enum. (Service: CognitoIdentityProvider, Status Code: 400, Request ID: xxxx)" (RequestToken: xxxx, HandlerErrorCode: InvalidRequest)
```

下記公式にも記載がある通り、ユーザープール作成後、必要な属性の変更やカスタム属性の削除には、新しいユーザープールを作成するか、削除して作り直す必要があります。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pool-updating.html

ユーザープールを削除して作り直す場合、Gen1 なら `amplify remove auth` -> `amplify push` -> `amplify add auth`でユーザープールを作り直せたのですが、Gen2 の場合、やり方がわかりません。

マニュアルや Amplify Discord チャンネルも探してみたのですが、見逃しているのか、まだ見つけられていないです。

- backend.ts の defineBackend から auth を削除してデプロイ -> デプロイエラー「The role with name undefined cannot be found.」IAMロールがない。
　
- AWSコンソールから Cognito を直接開き、対象の User pool を手動削除 -> デプロイエラー「User pool ap-northeast-1_51RECMyOq does not exist. 」ユーザープールがない。まあ、そうなるよね・・・。
　
- AWSコンソールから CloudFormation を直接開き、アプリに紐づくルートスタックを丸っと削除する。 -> デプロイ成功(間違って別のアプリや、関係のないスタックをくれぐれも消さないように)


登録済のユーザーや Storage、データベースのデータなど、バックエンドに登録されている全ての情報は消えてしまいますが、ユーザープールを含むバックエンドを再構築できました。力技です。

なお、この手順でバックエンドを再構築した場合、フロントも再デプロイが必要です。

ユーザープールのIDなどが変わっているため、フロントを再デプロイすることで、バックエンドへの接続情報が更新されます。

## Authenticator の username が使えない。

下記公式ページに Note があり、username フィールドは Gen1 Auth では動作するものの、Gen2 Auth では動作しないようです。

https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration#login-mechanisms

```
Note: A username, email, or phone_number value is required for Cognito User Pools. The username field will only work with Gen 1 Auth. For more information about using username see the docs.
```

実際に defineAuth を loginWith: { email: true } の状態で Authenticator に username フィールドを表示してユーザー新規登録した場合、username フィールドに入力した値が自動的にメールアドレスに置換されました。

このため、ユーザー名を User pool に持たせたい場合、下記公式にある通り、 preferred_username などを使います。

https://ui.docs.amplify.aws/react/connected-components/authenticator/customization#input-form-field-names-table

## backend.ts に Lambda API(REST)追加

基本的に、下記公式に従って進めます。

https://docs.amplify.aws/nextjs/build-a-backend/add-aws-services/rest-api/set-up-rest-api/

backend.ts に追記していくと行数が増えていくので、別ファイルに外だし可能です。

```backend.ts
...

const backend = defineBackend({
  auth,
  data,
  // Lambda API
  myApi,
});

...

/*
 * API
 */

configureAPI(backend);

```
```backend-api.ts
import { Backend } from '@aws-amplify/backend';
import { Stack } from 'aws-cdk-lib';
import {
  AuthorizationType,
  CognitoUserPoolsAuthorizer,
  Cors,
  LambdaIntegration,
  RestApi,
} from 'aws-cdk-lib/aws-apigateway';
import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { auth } from './auth/resource';
import { myApi } from './functions/my-api/resource';

export function configureAPI(
  backend: Backend<{
    myApi: typeof myApi;
    auth: typeof auth;
  }>,
) {
  // create a new API stack
  const apiStack = backend.createStack('api-stack');

  // create a new REST API
  const myRestApi = new RestApi(apiStack, 'RestApi', {

  ...
```

## 追加した Lambda API(REST) の実行で「CORS ヘッダー ‘Access-Control-Allow-Origin’ が足りない」エラーが出る。

追加した Lambda へ GET リクエストを出すものの、CORSエラー。

![スクリーンショット 2024-06-25 19.06.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/92e3afa6-550a-cd6b-05a2-69c847ad6fdd.png)

CORSエラーは開発環境 localhost からアクセスした場合のみ発生し、Amplify上でホストしている環境から Lambda へアクセスした場合はこの問題は発生しない。

Amplify公式記載の CDK で RestAPI を作成すると「プロキシ統合」の API Gateway
が作成されるため、基本的に CORS のヘッダーは Lambda API 内部の headers レスポンスの値がクライアントにそのまま返却されるはず。

![スクリーンショット 2024-06-25 19.09.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/de4bded7-591e-362f-3b6c-40b56b8bcb18.png)

![スクリーンショット 2024-06-25 19.12.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/7946677b-f154-9081-d9b9-01f74840e1f9.png)
(最初は Hono でリクエストを処理していたが、問題の原因が分からなかったので、
一旦取り外した)

もちろん、`Access-Control-Allow-Origin`も開発用として * を指定している。

ブラウザの開発者ツールで「ネットワーク」ログを確認すると、OPTIONSリクエストは成功しており、きっちり `Access-Control-Allow-Origin : *`で返却されている。

![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/550dbb4f-74b7-a7e6-d68b-daa2e82cc035.png)

次の GET リクエストで `Access-Control-Allow-Origin`ヘッダーが消えているため、いろいろ調べると、下記記事に従い API Gateway の「ゲートウェイレスポンス」へ CORS の設定をすることで、CORS エラーが消えて、実際に起こっているエラーをクライアントから取得できるようになった。
(設定変更後、AWSコンソールなどから API Gateway のデプロイを実行すること)

https://qiita.com/hshota28/items/08ff662f4c995e02ffb7

### 実際に起こっていた問題

下記のようなエラーが出ていました。

```
「Received 403 error response with payload: {"message":"User: arn:aws:sts::xxxxxxxxx:assumed-role/amplify-amplifybackend-no-amplifyAuthAPPTSGroupRole-XXXXXXXXXXXX/CognitoIdentityCredentials is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:ap-northeast-1:xxxxxxxxx:r0......../dev/GET/v1"}」
```

`amplify-amplifybackend-no-amplifyAuthAPPTSGroupRole-XXXXXXXXXXXX` に execute-api:Invoke 権限が存在していないとのこと。

CDK で権限を付与している部分があるのだけれど、対象のロール名を amplify_outputs.json へ出力して確認するも、それぞれ`amplify-amplifybackend-no-amplifyAuthauthenticatedU-XXXXXXXXXXXX`や`amplify-amplifybackend-no-amplifyAuthunauthenticate-XXXXXXXXXXXX`というロール名で、amplifyAuthAPPTSGroupRole ではない。

AWSコンソールから手動で`amplify-amplifybackend-no-amplifyAuthAPPTSGroupRole-XXXXXXXXXXXX`ロールへ execute-api:Invoke ポリシーをインラインなどで付与したら問題は解決したものの、CDK で本ロールに権限を付与したい。(コードは未作成)

```backend.ts
...
  // create a new resource path with Cognito authorization
  const booksPath = myRestApi.root.addResource('cognito-auth-path');
  booksPath.addMethod('GET', lambdaIntegration, {
    authorizationType: AuthorizationType.COGNITO,
    authorizer: cognitoAuth,
  });

  // create a new IAM policy to allow Invoke access to the API
  // localhost(aws sso利用時？)からアクセスする場合、下記ロールにも権限を付与する必要がある。
  // amplify-amplifybackend-no-amplifyAuthAPPTSGroupRole-XXXXXXXXXXXX
  const apiRestPolicy = new Policy(apiStack, 'RestApiPolicy', {
    statements: [
      new PolicyStatement({
        actions: ['execute-api:Invoke'],
        resources: [
          `${myRestApi.arnForExecuteApi('*', '/v1', 'dev')}`,
          `${myRestApi.arnForExecuteApi('*', '/v1/*', 'dev')}`,
          `${myRestApi.arnForExecuteApi('*', '/cognito-auth-path', 'dev')}`,
        ],
      }),
    ],
  });

  // attach the policy to the authenticated and unauthenticated IAM roles
  backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(
    apiRestPolicy,
  );
  backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy(
    apiRestPolicy,
  );

  // ユーザーのIAMロール名を取得
  const authenticatedUserRoleName =
    backend.auth.resources.authenticatedUserIamRole.roleName;
  const unAuthenticatedUserRoleName =
    backend.auth.resources.unauthenticatedUserIamRole.roleName;

  // add outputs to the configuration file
  backend.addOutput({
    custom: {
      API: {
        [myRestApi.restApiName]: {
          endpoint: myRestApi.url,
          region: Stack.of(myRestApi).region,
          apiName: myRestApi.restApiName,
        },
      },
      forDebug: {
        // デバッグ用出力(API動作に必要ではない)
        // 認証されたユーザーのIAMロール名を取得して出力
        AuthenticatedUserRoleName: authenticatedUserRoleName,
        UnAuthenticatedUserRoleName: unAuthenticatedUserRoleName,
      },
    },
  });
...
```

## Amplify UI の View を SSR できない。

Amplify UI の View コンポーネントは div タグの代わりにフランクに使えそう、と思ったのですが、実際に使うと SSR 時にエラーが出るため、'use client' するか、おとなしく div タグを使った方が良さそうです。

View の内部で react-hook-form を使っている雰囲気なのですが、何のために使っているのか、分からないです・・。

![スクリーンショット 2024-06-28 22.04.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/98b2dd7a-d8df-c336-9a45-532e3547f10a.png)

## SSR と RSC(React Server Components)

Next.js(SSR) + RSC で 'use client' を宣言すると Server Components と Clients Components との境界を宣言できるのですが、'use client' を宣言していても SSR はされる(サーバー側でもレンダリングされる)ので、クライアントでしかレンダリングされない宣言だと思っていると、実装時に Hydration Error が発生したりします。

ここは常識ということで、しっかり意識していきたいところです。

https://scrapbox.io/pokutuna/Next.js_App_Router_%E3%81%A7_Client_Component_%E3%82%92_SSR_%E3%81%99%E3%82%8B%E6%99%82%E3%81%AB_Cookie_%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E5%95%8F%E9%A1%8C

ps. Server Components(SC)、Client Components (CC)を意識するため、components ディレクトリで SC なら /sc ディレクトリ、CC なら /mc ディレクトリへ格納するようにしてみました。/cc ディレクトリにしなかったのは、Client Components はサーバーサイドでもレンダリングされたり、SC からロードされて SC としてふるまったりする可能性ありそうだったので、Mixed Components (MC) という造語を作って、一旦整理しています。

![スクリーンショット 2024-06-30 12.43.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490699/3ee5f20d-5be5-27b9-7cd0-80fed7fad410.png)

ディレクトリ分けや分類は、BCD Design や Atomic Design を参考に試行中です。

## Authenticator コンポーネントか withAuthenticator を使わないと Storage Managerコンポーネントでプライベートバケットにアクセスできない。

https://ui.docs.amplify.aws/react/connected-components/storage/storagemanager#private-or-protected-buckets

事前に Storage Manager をプライベートバケットで使う場合は、Authenticator でラップするのか、withAuthenticator で直接実行する必要があります。

紹介した YouTube のサンプルリポジトリでは、AuthProvider で全体をラップしているため、プライベートバケットを使うためには Authenticator か withAuthenticator が必要です。

```
function Sample({ onClose }: SampleProps) {
  ...

  return (
    <StorageManager
    acceptedFileTypes={['.pdf']}
    path="private/"
    maxFileCount={1}
    isResumable
    onUploadError={(error, file) => {
        showError(error);
    }}
    /> 
  );
}

export const SampleWithAuth = withAuthenticator(Sample);
```

https://github.com/ErikCH/CodeFirstTypeSafety/tree/main

## Storage Manager でアップロード時に Access Denied になる。

結論から言えば、解決策は下記にあります。

https://repost.aws/questions/QUABKRPBSLRSKMc-tGXEtSWg/issue-with-amplify-gen2-file-upload-using-storage-access-denied

defineStorage で認証済ユーザーにアクセス権を与え、Cognito 認証済ユーザーでログインし withAuthenticator を使っているにも関わらず、アップロード時に Access Denied になります。

```resource.ts
export const storage = defineStorage({
  name: 'sample',
  access: (allow) => ({
    'private/*': [
      allow.authenticated.to(['read', 'write', 'delete'])
    ],
  }),
});
```

Amplify Gen1 の頃にもあったのですが、ユーザーを Cognito グループへ所属させた場合、
それまでアクセスできていた Storage へアクセスできなくなり、別途 Storage のアクセス権にグループの追加が必要になります。

```resource.ts
export const storage = defineStorage({
  name: 'sample',
  access: (allow) => ({
    'private/*': [
      allow.authenticated.to(['read', 'write', 'delete']),
      allow.groups(['GROUP_A']).to(['read', 'write', 'delete']),
    ],
  }),
});
```

今回、サンドボックス環境で Storage へグループのアクセス権を追加したのですが、`This resource type is not supported for hotswap deployments`と表示されてホットデプロイできませんでした。

一度サンボボックスを削除してから作成して解決できたのですが、本番だと、どういう手順を踏めばいいのか気になります。

## アプリの構成

試行錯誤中
Next.js 14(SSR) + React 18 RSC/RCC + Amplify Gen2
言語: TypeScript
モノレポ: Nx 19
UIライブラリ: Amplify UI (Material UIは使ったことがあり、興味本位で Amplify UI 使ってみたい)
CSSフレームワーク: Tailwind CSS
状態管理: jotai (SWRも使ってみたい)
Webフレームワーク(LambdaやAPIのルーティング用): Hono
コンポーネントの分類整理: BCD(C) Design + Atomic Design

# おわり

ここまでで、バックエンド、フロントともにデプロイが成功することを確認できました。

ご覧いただきありがとうございました。