インフラよく分かってない奴がAmplify Gen2を使ってみた
はじめに
私は実務でバックエンドの実装に関わることが多いのですが、インフラは昔から苦手で、インフラが絡むような場面では、インフラエンジニアに頼り切っていて敬遠していました。AWSに関しては、簡単なVPCやEC2インスタンスを構築した経験がある程度で、CDKに至っては全くの初心者です。
そんな中、最近Amplify Gen2を利用する案件に携わることになりました。直接実装に関わっていないのですが、話に聞くとAmplify Gen2は、TypeScriptで簡単にバックエンドを構築できるサービスらしく、実際にどのようなものなのか自分自身の目で確かめてみたいと思うようになりました。
そこで「インフラはイマイチよく分かってないけど、Amplify Gen2って本当に簡単に使えるの?」という疑問を解消すべく、実際にAmplify Gen2を使って超シンプルなチャットアプリを作成し、デプロイしてみることにしました!
この記事では、AWSもCDKもイマイチ分かってない状態から、Amplify Gen2を使って超シンプルなチャットアプリを作成し、デプロイするまでの過程を記録します。
準備するもの
プロジェクトのセットアップを始める前に用意すべきものについて説明します。
1. AWSアカウント
- まだ持っていない方は、AWS公式サイトから作成してください。
- Amplifyのsandbox実行で必要なIAMユーザーをAdministratorAccess権限を付与して作成しておいてください(本当は最小権限の方が良いらしいが、今回は簡略化のためAdministratorAccessを付与します)
2. Node.js + pnpm
- インストールされているかターミナルで
node -v
と実行して確認してください。 - インストールされていない場合は、公式サイトからダウンロードしてインストールしてください。
- 今回パッケージマネージャーに
pnpm
を利用するので、Node.jsをインストールした後に、公式サイトを参考にインストールしてください。
3. AWS CLI
- インストールされているかターミナルで
aws --version
と実行して確認してください。 - インストールされていない場合は、公式ドキュメントを参考にインストールしてください。
Amplify Gen2プロジェクトのセットアップ
Amplify Gen2プロジェクトのセットアップ手順について説明します。
Amplifyには、Next.jsやNuxt.jsなどで簡単にプロジェクトを作成するためのテンプレートが用意されており、そちらを利用しても良いのですが、今回はせっかくなのでマニュアルインストールでプロジェクトをセットアップしていきます。
リポジトリ
今回開発したアプリのリポジトリはこちらです。
Next.jsのセットアップ
まずは、Next.jsのプロジェクトを作成します。
pnpmを使う場合は、以下のコマンドでプロジェクトを作成できます。
pnpm create next-app
質問形式の回答は以下のとおりです。
✔ What is your project named? … amplify-simple-message # 好きなプロジェクト名入力
✔ Would you like to use TypeScript? … No / Yes # Yesで回答
✔ Would you like to use ESLint? … No / Yes # Noで回答
✔ Would you like to use Tailwind CSS? … No / Yes # Yesで回答
✔ Would you like to use App Router? (recommended) … No / Yes # Yesで回答
✔ Would you like to use Turbopack for `next dev`? … No / Yes # Noで回答
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes # Yesで回答
✔ What import alias would you like configured? › @/* # 何も入力せずEnter
これで、Next.jsでのフロント開発の準備が整いました!
今回はLintやFomatterに関してはBiome.jsを利用しているので、ESLintはNoで回答しています。
Amplifyのセットアップ
次に、プロジェクトに移動してAmplifyでバックエンドを開発するパッケージなどをインストールしていきます。
まずは、Amplifyで認証・データモデル・Lambda関数・CDKなどを扱うのに必要なパッケージをインストールします。
pnpm add --save-dev @aws-amplify/backend @aws-amplify/backend-cli @types/aws-lambda aws-cdk aws-cdk-lib constructs esbuild tsx
pnpm add aws-amplify @aws-amplify/adapter-nextjs @aws-amplify/ui-react
# 今回Redisを利用するので``ioredis``をインストール
pnpm add ioredis
必要なパッケージは揃ったので、Amplifyのデプロイや実装に必要なファイルを、以下のNext.jsのテンプレートからコピーしてきます。
以下のファイルをコピーしてください。
-
./amplify
ディレクトリ丸ごと amplify.yml
tsconfig.json
は以下のように編集しました。
{
"compilerOptions": {
// ... 省略
"paths": {
"~/*": ["./*"],
"@/*": ["./src/*"],
"@amplify/*": ["./amplify/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
// amplifyディレクトリは除外しておく
"exclude": ["node_modules", "amplify"]
}
paths
オプションで@amplify/*
のパスエイリアスを設定することで、./amplify
ディレクトリ内のファイルを簡単にインポートできるようになります。
amplify.yml
は以下のように編集しました。
version: 1
backend:
phases:
preBuild:
commands:
- npm install -g pnpm
build:
commands:
- pnpm install --frozen-lockfile --store-dir .npm --prefer-offline
- pnpm ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
frontend:
phases:
preBuild:
commands:
- npm install -g pnpm
- pnpm install
build:
commands:
- pnpm run build
# 以下省略...
amplify.yml
はAmplify Hostingで継続的デプロイを行うための設定を記述するファイルです。今回は、pnpm
でデプロイするための設定に変更しています。
準備が完了したので、いざ実装を開始します!
実装前にインフラの説明
通常Amplify Gen2では、デフォルトでAppSync(GraphQL)のデータソースにDynamoDBを利用できるのですが、今回はCDKの学習も兼ねているので、以下のようなインフラでデータソースを扱いたいと思います(普通にDynamoDBを利用した方が圧倒的に簡単です)
- GraphQLのデータソースとしてLambda関数を利用
- Lambda関数からデータ保存/参照用にElastiCache(Redis)を利用
- Lambda関数とRedisを同じVPC上に配置してアクセス可能にする
- フロントではAppSync(GraphQL)経由でLambdaにアクセスしてデータのCURDを実行
スキーマ定義:Amplify Data
今回はチャットアプリなので、メッセージデータのスキーマ定義をします。
スキーマ定義は./amplify/data/resource.ts
ファイルに記述していきます。
通常DynamoDBをデータソースとする場合は、以下のようにスキーマを作成してdefineData
に設定します。
const schema = a.schema({
Message: a
.model({
id: a.integer().required(),
message: a.string().required(),
})
// データアクセスをApiキーで認可する
.authorization((allow) => [allow.publicApiKey()]),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
// データアクセスにApiキーによる認証を利用する
defaultAuthorizationMode: "apiKey",
apiKeyAuthorizationMode: {
expiresInDays: 30,
},
},
});
これだけで、DynamoDBにデータベースが作られ、定義したスキーマを利用することで、フロントから型安全にデータアクセスすることが可能になります。つまり、この時点でデータ定義とフロントで利用するApiの定義が完了しているんです!(簡単すぎる...)
ただ、今回はデータソースにLambdaを利用したいので、Lambda関数を作成していきます。
Lambda関数実装
今回は、以下の処理を行うLambda関数を作りたいと思います。
- メッセージの作成
- メッセージの削除
- メッセージ一覧取得
Lambda関数では、Redisへのアクセスを行うことになるので、複数の関数が存在すると面倒なので、今回はひとつのLabmda関数で複数の処理を実行できるようにします。つまり、ひとつのLambda関数にアクセスが来たら、値の内容によって、それぞれ操作別の関数に処理を振り分けるようにするということです。
1. Redisへのアクセスをする処理
2. それぞれの処理を行う関数
メッセージ作成
メッセージ削除 メッセージ一覧取得各関数の型は、のちに作成するスキーマ定義から取得することができるので、スキーマに従った関数の定義が可能です!
3. 処理振り分けをするLambda関数
Lambda関数の引数から渡ってきた各関数名と同じ名前のフィールド名(のちに記述するスキーマのプロパティ名)によって処理を振り分ける実装にしています(この方法で振り分けるのはあまり良くないかも?)
実行すると何故かEventオブジェクト型と若干違うデータが引数に渡されてしまっているので、無理やり型アサーションをしています。
4. Lambda関数のリソース設定
作成した関数をLambda関数リソースとして定義するにはdefineFunction
を使います。
これだけで、同じディレクトリに定義した./amplify/functions/message/handler.ts
をLambda関数リソースとして定義できます。
Lambda関数をデータソースとしたスキーマ定義
Lambda関数が定義できたので、この関数をデータソースとしたスキーマ定義をしていきます。
基本的には「このデータはこのデータソースを使ってこの型のデータを返します」のような設定をする感じです。
スキーマ定義にサブスクリプションというものが出てくるのですが、あまり理解していないので説明を省かせてください(すまん!)
AWSのリソース定義:CDK + Amplify Backend
データの準備ができたので、次にCDKを使ったAWSのリソースを定義していきます。
以下のようなリソースを定義したいと考えています。
この辺のインフラ設定はよく分からなかったので、AIに教えてもらいながら設定しました。
1. VPC
Lambda関数とRedisのためのネットワークを提供します。
プライベートなネットワーク環境を構築します。
2. サブネット
RedisクラスターとLambda関数を配置する場所を提供します。
プライベートサブネットのためインターネットへの直接アクセスは不可です。
3. セキュリティグループ
RedisクラスターとLambda関数のアクセスを制御するセキュリティーグループです。
4. Redisクラスター
データをキャッシュするためのインメモリデータストアです。今回はデータの永続化にも使用してみます(本来はキャッシング用途が主です)
5. Lambda関数
Lambda関数は既に作成済みなのですが、このリソースにRedisにアクセスするための情報を環境変数として設定したり、RedisへのアクセスできるようにVPCに置くための設定をします。
Sandbox
まずは、データのリソース定義をしたものを./amplify/backend.ts
に設定します。
import { data } from './data/resource.js';
const backend = defineBackend({
data,
});
defineData
やdefineFunction
でリソース定義したものをdefineBackend
に設定することで、細かいインフラの設定しなくてもAmplifyが良い感じにリソースを作成してくれます。
どのようなリソースが構築されるかを確認したい場合は、以下コマンドを実行してAmplifyのsandboxでデプロイすることができます(デプロイに数分かかります)
# <your profile> の部分は自身で設定したAWSのprofile名を設定してください
pnpm ampx sandbox --profile=<your profile>
もしも、リソースの設定に不備などあれば、デプロイ時にロールバックされるので、安心してデプロイできます!
sandboxを削除したい場合は、以下コマンドを実行してください。
pnpm ampx sandbox delete --profile=<your profile>
CDKでインフラ構築
認証やデータなどはAmplify標準の機能で簡単に実装できるのですが、それ以外のインフラの構築はCDKを使う必要があります...初心者の私には、これが非常に難しかった部分です(何度も設定ミスでデプロイに失敗しました)
基本的には、backend.createStack
で作成したスタック(CloudFormationスタック)に対して色々なリソースを定義していくことで、Amplifyのアプリのリソースとして定義できるみたいな感じです。
色々なリソースが簡単に定義可能
今まで紹介してきた、defineData
によるデータベースやdefineFunction
によるLambda関数の定義以外にも、Amplify Gen2では色々なリソースを簡単に定義できます。
Cognito認証
defineAuth
で簡単にCognitoのEメールログインを利用することが可能です。
GognitoではGoogleなどのソーシャルログインにも対応しているのですが、それもdefineAuth
で設定できます。
import { defineAuth } from "@aws-amplify/backend"
export const auth = defineAuth({
loginWith: {
email: true,
},
})
S3
defineStorage
で簡単にS3バケットを作成できます。
S3では、バケットのアクセス制御やストレージイベント監視なども設定できるのですが、それもdefineStorage
で設定できます。
mport { defineStorage } from '@aws-amplify/backend';
export const storage = defineStorage({
name: 'amplifyStorage'
});
これらのリソース設定は、データリソース定義と同じようにdefineBackend
に追加することで、デプロイ時にリソースが構築されます。
あと、今回は触れませんが、amplify-ui
パッケージを利用することで、これらのリソースとフロントの連携が簡単に実装できます。
フロント実装:Next.js
フロントエンドの実装について説明します。
インフラ構築だけでヘトヘトですが、まだフロントエンドの実装が残ってます。
画面説明
以下画面要件です。
- 「メッセージ」作成ボタンを押下して、表示されたダイアログにメッセージを入力すると一覧にメッセージが追加される。
- 一覧の各メッセージを押下するとメッセージが削除される。
- 同時に複数画面を立ち上げて操作しても、表示内容が同期する。
- メッセージを作成すると、他の画面ではメッセージが一覧に追加される
- メッセージを削除すると、他の画面ではメッセージが一覧から削除される
コンポーネント実装
まずは、メッセージの作成と削除をするためのMessageSectionクライアントコンポーネントを作成します。
デプロイをするとamplify_outputs.json
という設定ファイルが作成されるので、Amplify.configure
に設定することで、Amplifyのbackendで作成したデータなどがフロントで利用できるようになります。
そして、generateClient
で取得したオブジェクト経由でスキーマ定義したデータに型安全にアクセスができるようになります(素晴らしい仕組み!)
次にページコンポーネントを作成します。
基本的に作成したMessageSectionを設置するだけですが、初期データとしてサーバーコンポーネントでメッセージ一覧を取得して、MessageSectionのPropsに渡します。
サーバーコンポーネントでは、generateClient
ではなく、以下のようにサーバーサイド用のクライアントを取得する必要があります。
これで、フロントの実装完了したので、sandboxが起動した状態で以下コマンドを実行してみましょう!
pnpm dev
localhost:3000
にアクセスすると、超シンプルなチャット画面が表示されましたよね?
デプロイ
Amplifyでは、Githubに作ったリポジトリからデプロイが可能になっているので、実際に今回のプロジェクトをAmplifyでデプロイしてみました!
デプロイ方法については、色々な記事があるので、そちらを参照してください。
まとめ
今回は、Amplify Gen2を使って簡単なアプリを作成してみましたが、認証・データモデル作成 + データアクセス・Lambda関数など、インフラ意識せずスキーマなどを定義するだけで簡単に利用可能なところに感動しました。
ただ、Amplify Gen2でdefineXXX
のような関数が用意されていない場合、CDKを使ったAWSのサービスやインフラの知識が必要とされるリソース定義をする必要があり、インフラが苦手な私には難しいと感じていて 「これを使いこなしている弊社のフロントエンジニア優秀すぎだろ!?」 と尊敬の念が絶えませんでした。
Discussion