💭

Amplify Gen 2 と npm workspaces で monorepo を構成する

2024/09/07に公開

これは何

  • Amplify Gen 2 のドキュメントを読んでいたら monorepo のページがあったので、試してみた
  • npm workspaces で以下のような構成の monorepo を構築した
    • my-shared-backend: Amplify Gen 2 を使用してクラウドリソースを管理するパッケージ
    • my-react-app: 管理されているクラウドリソースを使用するフロントエンドアプリ
├── apps/
│   ├── my-react-app/
│   │   └── package.json
├── packages/
│   └── my-shared-backend/
│       ├── amplify/
│       │   ├── auth/
│       │   │   └── resource.ts
│       │   ├── data/
│       │   │   └── resource.ts
│       │   └── backend.ts
│       |── package.json
        └── tsconfig.json
└── package.json

Amplify Gen 2 の monorepo について

  • そもそも Amplify Hosting に monorepo に対応したデプロイ機能がある
  • Amplify Gen 2 の monorepo は、 Amplify Hosting で monorepo をデプロイする際にアプリ側で行うセットアップのナレッジという感じ
  • Amplify Gen 2 においてクラウドリソースは /amplify ディレクトリに作成される TypeScript ファイルなどで管理される
    • monorepo では /amplify ディレクトリを共有のワークスペースで管理することが推奨されている

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

Amplify Gen 2 supports monorepo workflows for fullstack builds with monorepo tools such as Nx and yarn workspaces. When building with Gen 2, we recommend creating the amplify/ folder in a shared workspace.

  • 今回は monorepo と言いつつもシンプルに試したいので、以下のような構成にする
├── apps/
│   ├── my-react-app/
│   │   └── package.json
├── packages/
│   └── my-shared-backend/
│       ├── amplify/
│       │   ├── auth/
│       │   │   └── resource.ts
│       │   ├── data/
│       │   │   └── resource.ts
│       │   └── backend.ts
│       |── package.json
        └── tsconfig.json
└── package.json

npm workspaces とは

  • npm workspaces は npm に標準で含まれている機能
  • 複数のパッケージを管理する機能で monorepo を構築できる

https://docs.npmjs.com/cli/v7/using-npm/workspaces

npm workspaces で monorepo を作る

monorepo のディレクトリを作成して初期化

mkdir mymonorepo && cd mymonorepo
npm init -y

適宜 GitHub など Amplify Hosting と連携可能な Git Provider にアップロードしておく

Amplify Gen 2 でバックエンドを構築する

npm workspaces で workspace を追加

npm init -y -w ./packages/my-shared-backend
cd ./packages/my-shared-backend

Amplify Gen 2 を使ってプロジェクトをセットアップする

npm create amplify@latest

プロジェクトのセットアップについては以下のドキュメントに詳細がある

https://docs.amplify.aws/react/start/manual-installation/

バックエンドをデプロイする

  • マネジメントコンソールで Amplify Hosting 画面を開いて、バックエンド用に新規で Amplify Hosting App を作成する
  • あとは Git プロバイダーと連携し、 monorepo としてデプロイするオプションを選択し、 packages/my-shared-backend ディレクトリを指定して進めればデプロイできる
  • 以下のドキュメントにスクリーンショットが掲載されている

https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/monorepos/#deploy-backend-app

これで最低限のバックエンド環境は構築完了
フロントエンドアプリのデプロイを行う際に Amplify Hosting App の ID が必要になるので控えておく

フロントエンドアプリを構築する

npm workspaces で workspace を追加

npm init -y -w ./apps/my-react-app
cd ./apps/my-react-app

vite で React アプリをセットアップ

# npm init -w で作成された package.json は使用しないので削除しておく
rm package.json
npm create vite@latest ./ -- --template react

# 動くか確認
npm install
npm run dev

フロントエンドアプリからバックエンドリソースを参照する

バックエンドをデプロイした Amplify Hosting App ID を指定して以下のコマンドを実行する

BACKEND-APP-ID=hogehoge
npx ampx generate outputs --branch main --app-id ${BACKEND-APP-ID}

これで amplify_outputs.json が生成されるので、フロントエンドアプリから読み込むことで、バックエンドリソースを参照できる

https://docs.amplify.aws/react/start/connect-to-aws-resources/

最小限のフロントエンドアプリを実装するため、以下のパッケージもインストールする

npm install aws-amplify @aws-amplify/ui-react

apps/my-react-app/src/App.jsx を以下のように編集する

import { Authenticator } from '@aws-amplify/ui-react';
import { Amplify } from 'aws-amplify';
import '@aws-amplify/ui-react/styles.css';
import outputs from "../amplify_outputs.json";

Amplify.configure(outputs)

function App() {
  return (
    <Authenticator>
      {({ signOut, user }) => (
        <main>
          <h1>Hello {user?.username}</h1>
          <button onClick={signOut}>Sign out</button>
        </main>
      )}
    </Authenticator>
  );
}

export default App

npm run dev でログイン動作を確認し、問題無さそうならデプロイに進む

フロントエンドアプリをデプロイする

  • マネジメントコンソールで Amplify Hosting を開いて、フロントエンドアプリ用に新規で Amplify Hosting App を作成する
  • 再度 Git プロバイダーと連携し、 monorepo としてデプロイするオプションを選択し、 apps/my-react-app ディレクトリを指定して進めればデプロイできる

クリーンアップ

以下を削除する

  • 作成した 2つ の Amplify Hosting App
    • バックエンドを管理している Amplify Hosting App を削除すると、クラウドリソースを管理している CloudFormation Stack も合わせて削除される
  • Git プロバイダーに作成したリポジトリ

感想とか注意点とか

  • ドキュメントを読み解くのが大変だったが、一度動かせてしまえば後は結構楽だった
  • Amplify Gen 2 で Cognito などの認証リソースを定義することが可能だが、これはアプリケーションと同じ寿命とは限らないため、 1つ のアプリコードと共存するのはアンマッチなケースがある
    • バックエンドを共有してフロントエンドアプリとモバイルアプリを開発する場合など
  • monorepo では Cognito を含むバックエンドリソースを monorepo 内の 1 パッケージとして管理できるので、バックエンドリソースを使う複数アプリと共存できる
    • バックエンドを使うアプリと同じリポジトリでコードを管理できるため、リソースの見通しが良い

Webhook 数の制限に注意

  • GitHub を使用する場合の注意点として Webhook の制限がある
  • GitHub には現状 1つ の repository に作成可能な Webhook の数は 20 までという制限がある
  • Amplify Hosting と連携する場合、 Amplify Hosting App 1つ に対して 1つ の Webhook が作成される
    • monorepo の場合、当たり前だが git repository は 1つ なので、例えば monorepo で 21 個のアプリを管理したい場合 Webhook が足りなくなってしまう
    • GitHub のドキュメントには Webhook が 20 以上必要になった場合、 GitHub から Webhook を受信してよしなに処理する Proxy を構築すれば良い的なことが書いてあるが、 Amplify Hosting の Webhook は Amplify Hosting が自動的に作成するものなので、ユーザー側で対処するのは難しそうな印象
      • Amplify Hosting 側に Issue が上がっているが、現状良い感じの解決策は無さそう
      • GitHub の Webhook でなく Amplify Hosting 側の Webhook を作成し、 GitHub 側に自動作成される Webhook をプロキシするという案がコメントされているが、パッと動かせるものでは無さそう

https://github.com/aws-amplify/amplify-hosting/issues/2572

よって GitHub で monorepo を管理する場合、 Webhook 数が 20 以下で足りそうか見積もっておく必要がある

試した環境

% sw_vers
ProductName:            macOS
ProductVersion:         14.6.1
BuildVersion:           23G93
% npm --version
10.8.1
% node --version
v20.16.0
% npx ampx --version
1.2.5
% npx ampx info
System:
  OS: macOS 14.6.1
  CPU: (11) arm64 Apple M3 Pro
  Memory: 119.22 MB / 18.00 GB
  Shell: /bin/zsh
Binaries:
  Node: 20.16.0 - /usr/local/bin/node
  Yarn: undefined - undefined
  npm: 10.8.1 - /usr/local/bin/npm
  pnpm: undefined - undefined
NPM Packages:
  @aws-amplify/auth-construct: 1.3.0
  @aws-amplify/backend: 1.2.1
  @aws-amplify/backend-auth: 1.1.3
  @aws-amplify/backend-cli: 1.2.5
  @aws-amplify/backend-data: 1.1.3
  @aws-amplify/backend-deployer: 1.1.0
  @aws-amplify/backend-function: 1.4.0
  @aws-amplify/backend-output-schemas: 1.2.0
  @aws-amplify/backend-output-storage: 1.1.1
  @aws-amplify/backend-secret: 1.1.1
  @aws-amplify/backend-storage: 1.1.2
  @aws-amplify/cli-core: 1.1.2
  @aws-amplify/client-config: 1.3.0
  @aws-amplify/deployed-backend-client: 1.4.0
  @aws-amplify/form-generator: 1.0.1
  @aws-amplify/model-generator: 1.0.5
  @aws-amplify/platform-core: 1.1.0
  @aws-amplify/plugin-types: 1.2.1
  @aws-amplify/sandbox: 1.2.1
  @aws-amplify/schema-generator: 1.2.1
  aws-amplify: 6.6.0
  aws-cdk: 2.155.0
  aws-cdk-lib: 2.155.0
  typescript: 5.5.4
AWS environment variables:
  AWS_STS_REGIONAL_ENDPOINTS = regional
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  AWS_SDK_LOAD_CONFIG = 1
No CDK environment variables

Discussion