🐡

lernaベースのmonorepoなNext.jsアプリケーションをAWS Amplify ConsoleでSSRホスティングする

5 min read 1

環境

Version
Node 16.1.0
npm 7.21.0
Next.js 11.1.2
lerna 4.0.0

構成

lernaベースのmonorepo構成で、Next.jsアプリケーションであるnext-app-1とnext-app-2に、module-1をlocal packageとして追加しています

my-lerna-repo/
  package.json
  packages/
    module-1/
      package.json
    next-app-1/
      package.json
    next-app-2/
      package.json

AWS Amplify Console

Amplify CLIでやる場合は、amplify add hostingの実行後、Hosting with Amplify Consoleを選択してリポジトリと連携することになると思います

Image from Gyazo

リポジトリ選択と、ブランチ選択は任意のものを選択し、今回はmonorepo構成なのでConnecting a monorepo? Pick a folder.にチェックを入れ、ホスティングするNextアプリのパスを入力します

Image from Gyazo

この時点で、以下のSelect an existing service role or create a new one so Amplify Console may access your resources.の選択項目が表示されていない場合、アプリがNextアプリとして判定されていないため、Nextアプリ配下のpackage.jsonを編集する必要があります

Image from Gyazo

AmplifyはアプリをNextアプリとして判定すると、上記の項目でサービスロールをアタッチすることを要求します。ここで必要な権限を満たすサービスロールをアタッチすることで、S3やCloudFront、LambdaといったNextアプリをホスティングするために必要なリソースを追加で作成してくれるのですが、アプリがNextアプリとして認識されていない場合、選択項目が表示されないので必要な権限をアタッチすることができず、ホスティングも失敗します

AmplifyにアプリをNextアプリとして判定してもらうためには、package.jsonのdependenciesnextが含まれている必要があります。なので、Select an existing service role or create a new one so Amplify Console may access your resources.の選択項目が表示されていない場合、一度AWS Amplify Consoleの設定をキャンセルし、Nextアプリ配下のpackage.jsonのdependenciesとしてnextを追加してから再度設定し直しましょう

lernaの場合、lerna add next --scope next-app-1ですね
以下のようになっていれば良いはずです
修正版

next-app-1/package.json
"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start"
},
"dependencies": {
  "module-1": "^1.0.0",
  "next": "^11.1.2"
}

ちなみに、このフレームワークがNextとして判定されない問題、AWS Amplify Consoleでリポジトリ連携後、Connecting a monorepo? Pick a folder.にチェックを入れず、プロジェクトルートのpackage.jsonを参照させることでNextアプリとして判定させることはできるのですが、アプリをビルドする際にThe module 'react' was not found.とNextアプリがnode_modulesを参照できずダメでした


さて、Nextアプリ配下のpackage.jsonのdependenciesとしてnextを追加しましたら、サービスロールをアタッチします

Create new roleから、基本デフォルト設定で問題ないはずです

Image from Gyazo

Image from Gyazo

Image from Gyazo


サービスロールのアタッチが済みましたら、続いてビルド設定ですが、
デフォルトの設定ではビルドできないので、下記のamplify.ymlになるように編集します
修正版

Image from Gyazo

amplify.yml
version: 1
applications:
  - frontend:
      phases:
        preBuild:
          commands:
            - npm install -g lerna
            - cd ../../
            - npm ci
	    - lerna bootstrap
        build:
          commands:
            - cd packages/next-app-1
            - npm run build
      artifacts:
        baseDirectory: .next
        files:
          - '**/*'
      cache:
        paths:
          - node_modules/**/*
    appRoot: packages/next-app-1

最後にAdvanced settingsです

環境変数のAMPLIFY_MONOREPO_APP_ROOTAMPLIFY_DIFF_DEPLOYは自動で設定されているはずなので変更は加えませんが、staging環境などproduction以外の環境としてホスティングしたい場合は、こちらでAPIのホストやサードパーティのcredentialを設定すると良さそうです

また、バージョン11系のNext.jsを利用している場合は、Live package updatesNext.js versionを選択し、Versionlatestにしましょう

Image from Gyazo

こんな感じでOKなはずです

Image from Gyazo


余談

  1. エラー周りのトラッカーとしてSentryを入れていたのですが、@next/sentryが入っていると、Starting SSR Build...とログが表示された直後にエラーでビルドが落ちてしまいました。ホスティングを優先したかったので一旦@next/sentrydependenciesから消してビルドしましたが、Sentryを利用している場合は注意です。また、何かご存知の方いましたら教えていただけると幸いです

https://github.com/aws-amplify/amplify-console/issues/1889
  1. 途中、試行錯誤の過程でlernaをやめてnpmのworkspacesで試したりもしていましたが、そちらはどうもビルドが上手くいきませんでした。npm workspacesベースのmonorepoでちゃんとSSRビルドできたよって方いましたら設定やamplify.ymlとか知りたいです...(lernaよりnpm workspacesの方がシンプルで良さそうな印象があり)

Discussion

ピン留めされたアイテム

追記

Nextアプリ配下のpackage.jsonのdependenciesとしてnextを追加することで、next buildコマンドによるproductionビルドは問題なく成功します。

しかし、開発環境でnext devコマンドによるビルドを行うとブラウザでHooks can only be called inside the body of a function component.とエラーが表示されました。

コンポーネントのトップレベル以外でhooksを呼び出すと見られるエラーではありますが、他の原因でも発現する場合があり、今回は、

同じアプリ内に 2 つ以上の React のコピーが含まれている。

のケースに該当しますので、コードを修正します。

next: <version>の部分をnext: "link:../../node_modules/next"に変更します。


next-app-1/package.json
"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start"
},
"dependencies": {
  "module-1": "^1.0.0",
  "next": "link:../../node_modules/next"
}

また、amplify.ymlの方も修正が必要です。

lerna bootstrapコマンドにオプション--no-ciを追加します。

amplify.yml
version: 1
applications:
  - frontend:
      phases:
        preBuild:
          commands:
            - npm install -g lerna
            - cd ../../
            - npm ci
	    - lerna bootstrap --no-ci
        build:
          commands:
            - cd packages/next-app-1
            - npm run build
      artifacts:
        baseDirectory: .next
        files:
          - '**/*'
      cache:
        paths:
          - node_modules/**/*
    appRoot: packages/next-app-1

以上です

ログインするとコメントできます