AWS Amplify Gen2 が 2024年 5月 に一般提供開始となりました (アナウンス)。いい機会ですので手元でちょっとしたデモアプリを作成するために、Amplify Gen2 も使ってみようと思いました。本記事ではかなり初歩的とは思いますがマニュアルインストールした際の手順を記録しておきたいと思います。
Amplify そのもの内容についてはほとんど触れませんのであしからず 🙇♂️
はじめに
公式ドキュメントから辿ると、“Get started” に “Quickstart” があり、テンプレートから簡単に始められそうなのですが、手元 (localhost) でちょっと試すために GitHub リポジトリ作るのものな…と思いましたので、マニュアルインストールをしていきます。
“Get started” には “Manual installation” もあるのですが、純粋に Amplify のインストールだけであり、React などのセットアップには言及されていません。この界隈に疎い私はいくつかエラーに遭遇したのでその記録を残しておきたいと思います。
おおまかな手順としてはこちらの Qiita の投稿を参考にさせていただきました。
ちなみに最近は npm
ではなく pnpm
を使っていますのでご了承ください。
検証したりする時 CDK で作ることが多いんですが、Laptop の容量がなくなってきたので node_modules 一掃して pnpm 使ってみることにしたhttps://t.co/NqTED9upEy
— msysh (@msysh) May 29, 2024
React プロジェクトの作成
まずはじめに React プロジェクトを作っていきます。昨今は Vite というもので React プロジェクトが作れるそうです。create-react-app
はもう公式でも案内されていないとか。
pnpm create vite@latest demo-app -- --template react-ts
実行すると demo-app
というディレクトリを作成し、その中に React プロジェクトを作成します。カレントディレクトリに作成したい場合は demo-app
部分を .
にします。
フレームワークを選択します。今回は React を選択します。
? Select a framework: › - Use arrow-keys. Return to submit.
Vanilla
Vue
❯ React
Preact
Lit
Svelte
Solid
Qwik
Others
TypeScript のつもりでしたが、SWC というのも出てきました。SWC は Babel などよりかなりパフォーマンス改善がされているそうです。良さそうなので選択してみます (よくわかっていない)。
? Select a variant: › - Use arrow-keys. Return to submit.
TypeScript
❯ TypeScript + SWC
JavaScript
JavaScript + SWC
Remix ↗
必要なものをインストールして Web アプリを立ち上げます。
cd demo-app
pnpm install
pnpm run dev
以下のように表示されたら、ブラウザで http://localhost:5173 を開いてみます。
❯ pnpm run dev
> demo-app@0.0.0 dev /tmp/demo-app
> vite
Port 5173 is in use, trying another one...
VITE v5.2.12 ready in 415 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
良さそうです。
Amplify Gen2 のセットアップ
続いて本題の Amplify Gen2 をインストール、セットアップしていきます。
Amplify Gen2 インストール
React プロジェクトのルートで (demo-app
ディレクトリの中で) 以下のコマンドを実行します。
pnpm install -D \
@aws-amplify/backend@latest \
@aws-amplify/backend-cli@latest
Amplify リソース関連のファイルを作成
Amplify で必要なファイルなどを作成していきます。プロジェクトのルートディレクトリで、amplify
ディレクトリを作成します。
mkdir amplify
amplify/backend.ts
という TypeScript ファイルを作成し、以下のように記述します。
|
|
サンドボックスの起動
(npm の場合は npx ampx sandbox
で起動します。)
pnpm exec ampx sandbox
エラーとなりました。。。
:
(snip)
:
undefined
ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command "tsx" not found
Did you mean "pnpm tsc"?
Error: Subprocess exited with error 254
Caused By: Subprocess exited with error 254
tsx
コマンドが見つからないとのことなのでインストールします。
pnpm install -D tsx
あらためてサンドボックスを起動します
pnpm exec ampx sandbox
(CDK を使ったことない場合は、ブートストラップを完了するよう URL が表示されるようです。ガイドに従ってくださいませ。)
警告なども少し出ますが、以下の状態のまま実行中になれば無事サンドボックスは起動できています。
:
(snip)
:
✨ Total time: 11.92s
[Sandbox] Watching for file changes...
File written: amplify_outputs.json
セットアップとしては以上です。この段階ではリソースを何も定義していないので、サンドボックス、すなわち AWS クラウド側にもリソースは作成されていません (CloudFormation スタックはできていますがメタデータしかありません)。
今後用途に応じて追加していくであろうパッケージは以下のようなものが一例になるでしょうか。
dependencies
- UI 関連
- aws-amplify
- @aws-amplify/ui-react
- SDK 関連 (xxx は AWS サービス名 (例: s3))
- @aws-sdk/client-xxx
- SigV4 でリクエストするとか (参考 : Call a GraphQL API from a Lambda function)
- @aws-crypto/sha256-universal
- @aws-sdk/credential-provider-node
- @smithy/protocol-http
- @smithy/signature-v4
- node-fetch
- UI 関連
devDependencies
- Amplify のスコープ外のリソース追加のため
- aws-cdk-lib
- Amplify のスコープ外のリソース追加のため
Lambda 関数の追加
いきなり Lambda を追加するのも変なのですが、Lambda 関数を追加した際にエラーが出てしまったのでトラブルシュートの記録として残しておきたいと思います。
プロジェクトのルートフォルダから amplify/function/my-function/resource.ts
を作成し、以下のように記述します。
|
|
続いて entry: './handler.ts',
とあるように Lambda 関数の実態を amplify/function/my-function/handler.ts
に作成します。
(ちなみに、handler.ts
という名前でファイルを作成した場合は、entry: './handler.ts',
の記述を省略できます。)
|
|
参照している依存も追加しておきます。
pnpm install -D aws-lambda @types/aws-lambda
冒頭で作成した amplify/backend.ts
に、作成した Lambda 関数を追加します (2, 5行目を追加)。
|
|
すると、サンドボックスで以下のようなエラーが出てしまいました (見やすさのため改行させています。実際は 1行でフラットに出力)。
Failed to instantiate nodejs function construct
Caused By: Failed to bundle asset amplify-demoapp-XXX-sandbox-e7d4f2307f/function/my-function-lambda/Code/Stage,
bundle output is located at /Users/XXX/tmp/demo-app/.amplify/artifacts/cdk.out/bundling-temp-XXXXXXXX-error:
Error: bash -c pnpm exec -- esbuild --bundle "/Users/XXX/tmp/demo-app/amplify/function/my-function/handler.ts"
--target=node18 --platform=node --format=esm
--outfile="/Users/XXX/tmp/demo-app/.amplify/artifacts/cdk.out/bundling-temp-XXXXXXXX/index.mjs"
--external:@aws-sdk/* --loader:.node=file
--banner:js="
/**
* Reads SSM environment context from a known Amplify environment variable,
* fetches values from SSM and places those values in the corresponding environment variables
*/
export const internalAmplifyFunctionResolveSsmParams = async (client) => {
const envPathObject = JSON.parse(process.env.AMPLIFY_SSM_ENV_CONFIG ?? '{}');
const paths = Object.keys(envPathObject);
if (paths.length === 0) {
return;
}
let actualSsmClient;
if (client) {
actualSsmClient = client;
}
else {
const ssmSdk = await import('@aws-sdk/client-ssm');
actualSsmClient = new ssmSdk.SSM();
}
const resolveSecrets = async (paths) => {
const response = await actualSsmClient.getParameters({
Names: paths,
WithDecryption: true,
});
if (response.Parameters && response.Parameters.length > 0) {
for (const parameter of response.Parameters) {
if (parameter.Name) {
const envKey = Object.keys(envPathObject).find((key) => envPathObject[key].sharedPath === parameter.Name);
const envName = envKey? envPathObject[envKey].name : envPathObject[parameter.Name]?.name;
process.env[envName] = parameter.Value;
}
}
}
return response;
};
const response = await resolveSecrets(paths);
const sharedPaths = (response?.InvalidParameters || [])
.map((invalidParam) => envPathObject[invalidParam].sharedPath)
.filter((sharedParam) => !!sharedParam);
if (sharedPaths.length > 0) {
await resolveSecrets(sharedPaths);
}
};
await internalAmplifyFunctionResolveSsmParams();
const SSM_PARAMETER_REFRESH_MS = 1000 * 60;setInterval(() => {
void internalAmplifyFunctionResolveSsmParams();
}, SSM_PARAMETER_REFRESH_MS);
export {};"
--inject:"/Users/XXX/tmp/demo-app/node_modules/.pnpm/@aws-amplify+backend-function@1.0.3_@aws-sdk+types@3.577.0_aws-cdk-lib@2.144.0_constructs@10._uc5ex6achjb4y3focymv7hus6i/node_modules/@aws-amplify/backend-function/lib/lambda-shims/cjs_shim.js"
run in directory /Users/XXX/tmp/demo-app exited with status 254
Resolution: See the underlying error message for more details.
Error: bash -c pnpm exec -- esbuild ...
というところに着目して、 esbuild をインストールしたところエラーなく Lambda 関数がデプロイできました。
pnpm install -D esbuild
サンドボックスの削除
サンドボックスは AWS クラウド側では CloudFormation スタックとして存在しています。ローカル側でサンドボックスのプロセス (ampx
) を終了させても、CloudFormation スタックが消えるわけではありません。スタックを削除するには以下のコマンドを実行します。
pnpm exec ampx sandbox delete
確認が入るので “y” を押します。
? Are you sure you want to delete all the resources in your sandbox environment (This can't be undone)? (y/N)
まとめ
vite を使って Amplify Gen2 のプロジェクトをセットアップしてみました。私の環境では tsx や esbuild などの追加も必要でした。同じようなエラーに遭遇した方のお役に立てたら幸いです。
最後に・・・
この投稿は個人的なものであり、所属組織を代表するものではありません。ご了承ください。