Amazon Web Services ブログ
AWS CDK で NextJS 13 のアプリケーションを Amplify にデプロイする
この記事は、Deploy a NextJS 13 application to Amplify with the AWS CDK を翻訳したものです。
近年のアプリケーション開発では、認証、API セットアップ、ファイルストレージなどの機能がしばしば含まれます。前回の記事では、AWS Amplify と AWS CDK が、これらのサービスを立ち上げるための他との差別化につながらない作業をどのように管理するかを見ました。
しかし、ホスティングプラットフォームがなければ、あなたの顧客はあなたの製品を目にすることはないでしょう。幸い、AWS Amplify Hosting は、DNS ルーティングの管理、キャッシュの即時無効化、ステージング環境の管理、Git プロバイダーによる CI/CD など、様々な機能を備えたプラットフォームです。
コンソールをクリックしてアップすることもできますが、今回は AWS CDK を使って、NextJS React フレームワークを使ったフロントエンドをデプロイします。また、バックエンドからフロントエンドに環境変数を渡して、Amplify JavaScript ライブラリを利用して環境変数を利用できるようにします。
アプリケーションの概要

アプリケーションには、ほとんどのフルスタックアプリケーションが恩恵を受けるであろうマネージドサービスを備えたバックエンドが含まれます。
- Amazon Cognito: ユーザープールによるユーザーサインインと、ID プールによるサービス認証
- AWS AppSync: WebSocket によるリアルタイムサポートを提供する、当社のマネージド GraphQL API
- Amazon DynamoDB: データを永続化するための NoSQL データベース
- Amazon CloudFront: S3 バケットに保存されたファイルのアセットキャッシング
- Amazon Simple Storage Service (S3): ファイルストレージ。公開画像は CloudFront で配信され、保護されたコンテンツは事前署名された URL で配信されます。
このリポジトリを使用するには、CDK Fullstack Kitchen Sinkリポジトリをクローンし、プロジェクトのディレクトリで次のコマンドを実行します。
npm install
パッケージのインストールが完了したら、コードエディタでプロジェクトを開いてください。
スタックのセットアップ
既存のサービススタックは、bin/cdk-kitchen-sink.tsファイルに配置されています。各スタックには独自の実装の詳細があり、それを見ることができます。この値を変更することで、利用者は自身のニーズに合わせてアプリケーションを簡単に移植することができます。
今回は、既存のフロントエンドリポジトリを Amplify Hosting にデプロイするため、AmplifyHostingStack を作成し、必要な変数を指定します。
// bin/cdk-kitchen-sink.ts
//… other stack files
const amplifyHostingStack = new AmplifyHostingStack(app, 'HostingStack', {
  githubOauthTokenName: 'github-token',
  owner: ‘your-user-name',
  repository: 'your-frontend-repo-name’,
  environmentVariables: {
    USERPOOL_ID: authStack.userpool.userPoolId,
    GRAPHQL_URL: apiStack.graphqlURL,
  },
})元々あるスタックを利用するスタックを作成するために、必要な全ての値をここで作成しておきます
フロントエンドのリポジトリを GitHub から Amplify Hosting にデプロイするには、リポジトリ名とオーナー、そして GitHub 認証トークンへの参照を指定しなければなりません。さらに、スタックから値を引っ張ってきて環境変数として設定することもできます。
環境変数は暗号化されて保存されており、ビルド後に process.env プロパティを通じてフロントエンドアプリケーションから利用できます。
構成の理解
さて、スタックをどのように作成するか理解できたところで、実際に作ってみましょう。
NextjsHostingStack.ts というファイルを新規に作成し、以下を追加します。
import { CfnOutput, SecretValue, Stack, StackProps } from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as codebuild from 'aws-cdk-lib/aws-codebuild'
import {
  App,
  GitHubSourceCodeProvider,
  RedirectStatus,
} from '@aws-cdk/aws-amplify-alpha'
interface HostingStackProps extends StackProps {
  readonly owner: string
  readonly repository: string
  readonly githubOauthTokenName: string
  readonly environmentVariables?: { [name: string]: string }
}上記のスニペットでは、このファイルで使用するモジュールをインポートし、スタックに渡されるプロパティの型情報を定義しています。(これらは bin ディレクトリで定義したものと一致することに注意してください)。
このプロジェクトは、AWS Amplify の alpha CDK construct を利用しています。これは npm install を実行するとインストールされました。
 次に、スタックを作成します。
export class AmplifyHostingStack extends Stack {
  constructor(scope: Construct, id: string, props: HostingStackProps) {
    super(scope, id, props)
    const amplifyApp = new App(this, 'AmplifyCDK', {
      appName: 'NextJS app from CDK',
      sourceCodeProvider: new GitHubSourceCodeProvider({
        owner: props.owner,
        repository: props.repository,
        oauthToken: SecretValue.secretsManager(props.githubOauthTokenName),
      }),
      autoBranchDeletion: true,
    })
  }
}これでスタックができあがり、App コンストラクトを使用して Amplify Hosting サービスに接続できるようになりました。App オブジェクトには今後もいろいろと追加していく予定ですが、とりあえず GitHub をソースコードの提供元として設定しています。これによって、フロントエンドリポジトリの CI/CD が可能になります。autoBranchDeletion を true に設定していることにも注目しましょう。これは、接続されているブランチが GitHub 上で削除されると、Amplify Hosting でも自動的に削除されることを意味します。
次に、「もしユーザーが存在しないページにアクセスしたら、404 (Not Found) エラーでリダイレクトする」というルーティングルールを設定します。また、環境変数を渡せるようにします。
App コンストラクトのプロパティに以下を追加してください。
customRules: [
  {
    source: '/<*>',
    target: ' /index.html',
    status: RedirectStatus.NOT_FOUND_REWRITE,
  },
],
environmentVariables: props.environmentVariables,フロントエンドフレームワークは数多く存在し、そのビルドプロセスも様々です。そこで、プロジェクトをどのようにビルドしたいか、どのアーティファクトが重要か、将来のデプロイを高速化するために何をキャッシュすべきかを、Amplify に伝える必要があります。
これはbuildspec.yml ファイルを Amplify Hosting に提供することで実現できます。幸い、多くの開発者は YAML と別の言語を混在させたくないので、Amplify のコンストラクトはオブジェクトを YAML 形式に変換する便利なユーティリティを提供します。
App コンストラクトのプロパティに以下を追加してください。
buildSpec: codebuild.BuildSpec.fromObjectToYaml({
  version: 1,
  frontend: {
    phases: {
      preBuild: {
        commands: ['npm ci'],
      },
      build: {
        commands: ['npm run build'],
      },
    },
    artifacts: {
      baseDirectory: '.next',
      files: ['**/*'],
    },
    cache: {
      paths: ['node_modules/**/*'],
    },
  },
})設定は以上です。これで、NextJS アプリケーションの再利用可能なデプロイ方法ができました。
デプロイ時に、Amplify Hosting に、どのブランチが Production ブランチであるかを伝えることができます。また、デプロイ時に、アプリの appId を出力させることもできます。そのためには、スタックファイルの最後(スタックの中)に、以下を追加します。
amplifyApp.addBranch('main', {
  stage: 'PRODUCTION',
})
new CfnOutput(this, 'appId', {
  value: amplifyApp.appId,
})このページの完成版についてはこのプロジェクトのリポジトリの最終ブランチをご確認下さい。
GitHub シークレットの作成と保存
先ほど説明したように、このアプリケーションではフロントエンドリポジトリをプルしたりプロジェクトをビルドしたりするための権限が必要です。GitHub アカウントの新しいトークンを作成するには、このリンクをクリックします。
権限は admin:repo_hook 権限セットを選択します。トークンに名前をつけ、ボタンをクリックしてトークンを作成したら、生成されたトークンをクリップボードにコピーします。
このシークレットを AWS Secrets Manager に保存しておきます。AWS CLI を使用してシークレットを作成することもできますが、ここでは AWS コンソールを使用します。
Secrets Manager で、「Store a new secret」を選択すると、以下の画面が表示されます。

上のスクリーンショットのように、「Other type of secret 」のオプションを選択し、タブをクリックすると、Key/Value ではなく Plaintext としてシークレットを入力します。
次の画面では、HostingStack で指定したものと一致する名前をシークレットに付けます。今回の場合、github-token という名前にします。
デプロイ
このソリューションを試すために、次のコマンドで NextJS アプリケーションを新規に作成します。
npx create-next-app simple-nextjs
リポジトリを作成したら、GitHub に保存し、バックエンドの bin/cdk-kitchen-sink.ts ファイルで owner と respository フィールドが一致することを確認します。
設定後、アプリケーションをデプロイするために、以下のコマンドを実行します。
npx aws-cdk deploy --all
注:この投稿の時点では、NextJS 13の機能を有効にする新しいホスティングランタイムを利用するために、Amplify Hosting アプリの platform プロパティを更新する CLI コマンドを実行する必要があります。デプロイすると、Hosting アプリの appId がコンソールに出力されるはずです。その値を使って、CLI で次のコマンドを実行します。
aws amplify update-app -app-id THE_APP_ID -platform WEB_COMPUTE
この記事は、CloudFormation にその値が存在するようになった時点で更新されます。

クリーンアップ
作成されたバックエンドリソースを削除するには、以下のコマンドを実行します。
npx aws-cdk destroy --all
また、Secrets Manager に保存されている秘密鍵を削除する場合は、別途費用が発生します。
まとめ
この投稿では、NextJS 13 アプリケーションを Amplify Hosting にデプロイする、再利用可能な CDK スタックを作成しました。これによって、フロントエンドのアプリケーションは、サービスコンシューマであり続けながら、デプロイ時間を短縮することができます。NextJS 13 アプリケーションをサポートするために、新しい WEB_COMPUTE プロパティを使用するようにプラットフォームを構成しました。これにより、デプロイ時間の短縮、より多くの機能、信頼性の向上が可能になります。Amplify Hosting と新しくなった NextJS の機能については、AWS Amplify Hosting ドキュメントを参照してください。