Amplify Gen2 React TypeScript auth function (amplify-qrticket)
2024.05.19 @moritalous 手を動かして0から理解するAmplify Gen2
トレースして学習します。
- 2024.06.07(金)~
- 2024.06.07
React TypeScriptプロジェクトを最新版の Vite で作成
$ npm create vite@latest amplify-qrticket -- --template react-ts
Need to install the following packages:
create-vite@5.2.3
Ok to proceed? (y) y
Scaffolding project in /home/ec2-user/environment/amplify-qrticket...
Done. Now run:
cd amplify-qrticket
npm install
npm run dev
$ cd amplify-qrticket/
$ ls -ls
total 24
4 -rw-r--r--. 1 ec2-user ec2-user 1300 Jun 6 04:49 README.md
4 -rw-r--r--. 1 ec2-user ec2-user 366 Jun 6 04:49 index.html
4 -rw-r--r--. 1 ec2-user ec2-user 751 Jun 6 04:49 package.json
0 drwxr-xr-x. 2 ec2-user ec2-user 22 Jun 6 04:49 public
0 drwxr-xr-x. 3 ec2-user ec2-user 104 Jun 6 04:49 src
4 -rw-r--r--. 1 ec2-user ec2-user 605 Jun 6 04:49 tsconfig.json
4 -rw-r--r--. 1 ec2-user ec2-user 233 Jun 6 04:49 tsconfig.node.json
4 -rw-r--r--. 1 ec2-user ec2-user 163 Jun 6 04:49 vite.config.ts
依存関係をインストールする
$ npm install
npm WARN deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm WARN deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm WARN deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
added 214 packages, and audited 215 packages in 14s
41 packages are looking for funding
run npm fund
for details
found 0 vulnerabilities
開発サーバーを起動する
$ npm run dev
VITE v5.2.12 ready in 505 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
vite.config.ts の内容を変更
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
// cloud9が許可するポート番号
server: { ●追加
port: 8080, ●追加
}, ●追加
plugins: [react()],
})
開発サーバーを起動する
$ npm run dev
amplify-qrticket@0.0.0 dev
vite
VITE v5.2.12 ready in 505 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
5:05:49 AM [vite] vite.config.ts changed, restarting server...
5:05:49 AM [vite] server restarted.
➜ Local: http://localhost:8080/
➜ Network: use --host to expose
- 2024.06.06(木) 作業メモ
@aws-amplify/backend をインストール
npm add --save-dev @aws-amplify/backend@latest
npm WARN deprecated @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
npm WARN deprecated @babel/plugin-proposal-object-rest-spread@7.20.7: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.
npm WARN deprecated core-js@2.6.12: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
added 439 packages, and audited 772 packages in 59s
119 packages are looking for funding
run npm fund
for details
found 0 vulnerabilities
@aws-amplify/backend-cli をインストール
npm add --save-dev @aws-amplify/backend-cli@latest
added 440 packages, and audited 1212 packages in 35s
163 packages are looking for funding
run npm fund
for details
found 0 vulnerabilities
backend を作成
~/environment/amplify-qrticket $ mkdir amplify
~/environment/amplify-qrticket $ touch amplify/backend.ts
- backend.ts を新規に作成する。(設定内容はなし)
import { defineBackend } from '@aws-amplify/backend';
defineBackend({});
sandboxを作成
- npm run devを実行中のターミナルと別のターミナルを開き、以下のコマンドを実行
~/environment/amplify-qrticket $ npx ampx sandbox
[Sandbox] Pattern !.vscode/extensions.json found in .gitignore. ".vscode/extensions.json" will not be watched if other patterns in .gitignore are excluding it.
Amplify Sandbox
Identifier: ec2-user
Stack: amplify-amplifyqrticket-ec2user-sandbox-7052f44ddd
To specify a different sandbox identifier, use --identifier
✨ Synthesis time: 0.06s
⚠️ The --hotswap and --hotswap-fallback flags deliberately introduce CloudFormation drift to speed up deployments
⚠️ They should only be used for development - never use them for your production Stacks!
●省略
⚠️ The following non-hotswappable changes were found:
logicalID: deploymentType, type: Stack Output, reason: output was changed
logicalID: region, type: Stack Output, reason: output was changed
logicalID: CDKMetadata, type: AWS::CDK::Metadata, reason: resource 'CDKMetadata' was created by this deployment
●省略
✅ amplify-amplifyqrticket-ec2user-sandbox-7052f44ddd
✨ Deployment time: 10.94s
Outputs:
amplify-amplifyqrticket-ec2user-sandbox-7052f44ddd.deploymentType = sandbox
amplify-amplifyqrticket-ec2user-sandbox-7052f44ddd.region = ap-northeast-1
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:053423220213:stack/amplify-amplifyqrticket-ec2user-sandbox-7052f44ddd/717d2b60-23c6-11ef-8107-0e3d6a90d7dd
✨ Total time: 11s
[Sandbox] Watching for file changes...
File written: amplify_outputs.json
- このブラウザタブが動作している限り、sandbox が起動している。
- 以下 別のターミナルで作業する。
~/environment/amplify-qrticket $ cat amplify_outputs.json
{
"version": "1"
}
backend に認証機能(Cognito)を追加
- ディレクトリ、ファイルを新規に作成する
~/environment/amplify-qrticket/amplify $ mkdir auth
~/environment/amplify-qrticket/amplify $ touch auth/resource.ts
- amplify/auth/resource.tsを新規に作成する
import { defineAuth } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true
}
});
- amplify/backend.tsにauthを追加する
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource'; ●追加
defineBackend({});●削除
defineBackend({●追加
auth,●追加
});●追加
- Credential 期限切れで反映されず
- Cloud9 の Credentialを 一時的なものから IAMユーザのものに変更して正常化
frontend に認証機能(Cognito)を追加
- @aws-amplify/ui-reactをインストールする
npm add @aws-amplify/ui-react
- src/main.tsxを編集する(Amplifyの設定を読み込む)
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import '@aws-amplify/ui-react/styles.css' ●追加
import { Amplify } from 'aws-amplify' ●追加
import outputs from '../amplify_outputs.json' ●追加
Amplify.configure(outputs) ●追加
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
- src/App.tsxを編集する(Authenticatorコンポーネントを追加)
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
+ import { Authenticator } from '@aws-amplify/ui-react'●追加
function App() {
const [count, setCount] = useState(0)
return (
+ <Authenticator>●追加
+ {({ signOut, user }) => (●追加
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
+ )}●追加
+ </Authenticator> ●追加
)
}
export default App
- 開発サーバでアプリケーションを起動する。
$ npm run dev
amplify-qrticket@0.0.0 dev
vite
VITE v5.2.12 ready in 650 ms
➜ Local: http://localhost:8080/
➜ Network: use --host to expose
➜ press h + enter to show help4:25:45 AM [vite] ✨ new dependencies optimized: @aws-amplify/ui-react
4:25:45 AM [vite] ✨ optimized dependencies changed. reloading
2024.06.12(木)
- sandboxを起動する。
$ npx ampx sandbox
-
Preview Runnning Application をクリックして開発サーバでプレビューする。
-
認証画面でメールアドレスとパスワードを入力してアカウントを新規作成する。
-
受信メールに記載された認証コード(数字)を入力する。
-
ログイン画面が表示される。
-
management console dognito で ユーザプールを確認する。
- 2024.06.12(水)
function (Lambda関数)をTypeScriptで追加
- amplify sandboxを起動する。
$ npx ampx sandbox
- aws-lambda ライブラリをインストールする。
$ npm add --save-dev aws-lambda
up to date, audited 1529 packages in 7s
170 packages are looking for funding
run npm fund
for details
found 0 vulnerabilities
- Lambda関数 amplify/function/hello/resource.tsを作成する
import { defineFunction } from '@aws-amplify/backend';
export const hello = defineFunction({
name: 'hello',
entry: './handler.ts',
})
- Lambda関数 amplify/function/hello-world/handler.tsを作成する。
import { Handler } from 'aws-lambda';
export const handler: Handler = async (event, context) => {
return { message: 'Hello, World' }
}
- backend.ts に function を追加する
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { hello } from './function/hello/resource' ●追加
const backend = defineBackend({ ●修正
auth,
hello, ●追加
});
認証ユーザにLambda 関数の実行権限を付与
- 認証ユーザに作成したLambda関数の実行権限を付与する。
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { hello } from './function/hello/resource'
const backend = defineBackend({
auth,
hello,
});
//認証されたユーザーのIAMロールの取得 ●追加
const authenticatedUserIamRole = backend.auth.resources.authenticatedUserIamRole;
//Lambda関数への実行権限の付与 ●追加
backend.hello.resources.lambda.grantInvoke(authenticatedUserIamRole);
amplify_outputs.jsonにLambda関数名を出力
- amplify_outputs.jsonにLambda関数名を出力する。
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { hello } from './function/hello/resource'
const backend = defineBackend({
auth,
hello,
});
//認証されたユーザーのIAMロールの取得
const authenticatedUserIamRole = backend.auth.resources.authenticatedUserIamRole;
//Lambda関数への実行権限の付与
backend.hello.resources.lambda.grantInvoke(authenticatedUserIamRole);
//
backend.addOutput({
custom: {
helloFunctionName: backend.hello.resources.lambda.functionName,
},
});
- amplify_outputs.json の出力を確認する。
{
"auth": {
"user_pool_id": "ap-northeast-1_L**",
"aws_region": "ap-northeast-1",
"user_pool_client_id": "36senjs1893d0mmgkj***",
"identity_pool_id": "ap-northeast-1:48d676ec-e3e8-4d91-95a6-***",
"standard_required_attributes": [
"email"
],
"username_attributes": [
"email"
],
"user_verification_types": [
"email"
],
"password_policy": {
"min_length": 8,
"require_numbers": true,
"require_lowercase": true,
"require_uppercase": true,
"require_symbols": true
},
"unauthenticated_identities_enabled": true
},
"version": "1",
"custom": {
"helloFunctionName": "amplify-amplifyqrticket-ec2use-hel***" ●確認した
}
}
frontend src/app.tsxを変更する
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import { Authenticator } from '@aws-amplify/ui-react'
import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda' ●追加
import { fetchAuthSession } from 'aws-amplify/auth' ●追加
import outputs from "../amplify_outputs.json" ●追加
function App() {
const [count, setCount] = useState(0)
●追加 ここから
const [text, setText] = useState("")
async function invokeHelloWorld() {
const { credentials } = await fetchAuthSession()
const awsRegion = outputs.auth.aws_region
const functionName = outputs.custom.helloFunctionName
const labmda = new LambdaClient({ credentials: credentials, region: awsRegion })
const command = new InvokeCommand({
FunctionName: functionName,
});
const apiResponse = await labmda.send(command);
if (apiResponse.Payload) {
const payload = JSON.parse(new TextDecoder().decode(apiResponse.Payload))
setText(payload.message)
}
}
●追加 ここまで
return (
<Authenticator>
{({ signOut, user }) => (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
<p>●追加
<button onClick={invokeHello}>invokeHello</button>●追加
<div>{text}</div>●追加
</p>●追加
</>
)}
</Authenticator>
)
}
export default App
- ブラウザでプレビューし、ボタンをクリックすると Lambda 関数が呼び出され結果が表示される。
- 2024.06.13(木)
- Cloud9の環境を確認する。
$ node --version
v18.18.2
$ npm --version
9.8.1
$ aws --version
aws-cli/2.15.10 Python/3.11.6 Linux/6.1.66-93.164.amzn2023.x86_64 exe/x86_64.amzn.2023 prompt/off
- sandboxを起動する。
$ npx ampx sandbox
- 開発サーバを起動する。
$ npm run dev
amplify-qrticket@0.0.0 dev
vite
VITE v5.2.12 ready in 482 ms
➜ Local: http://localhost:8080/
➜ Network: use --host to expose
➜ press h + enter to show help
Bedrockを呼び出す関数を追加
backend( Bedrockを呼び出す関数を追加)
- @aws-sdk/client-bedrock-runtime パッケージをインストールする。--save-dev オプションにより、開発中のみに必要とされ、本番環境での実行時には含まれない。
npm add --save-dev @aws-sdk/client-bedrock-runtime
added 85 packages, and audited 89 packages in 6s
2 packages are looking for funding
run npm fund
for details
found 0 vulnerabilities
- ディレクトリ、ファイルを作成する。
$ mkdir amplify/function/bedrock
$ touch amplify/function/bedrock/resource.ts
$ touch amplify/function/bedrock/handler.ts
import { defineFunction } from '@aws-amplify/backend';
export const bedrock = defineFunction({
name: 'bedrock',
entry: './handler.ts',
})
// AWS Lambdaのハンドラーとコンテキスト型をインポート
import { Context, Handler } from 'aws-lambda';
// Node.jsのストリームライブラリからWritableクラスをインポート
import { Writable } from 'stream';
// AWS SDKからBedrockRuntimeClientとInvokeModelWithResponseStreamCommandをインポート
import {
BedrockRuntimeClient,
InvokeModelWithResponseStreamCommand,
} from "@aws-sdk/client-bedrock-runtime";
// イベントタイプを定義しprompt 文字列を持つオブジェクト
type eventType = {
prompt: string
}
// 使用するモデルのIDを定義
const modelId = "anthropic.claude-3-haiku-20240307-v1:0"
// Lambda関数のハンドラを非同期関数として定義
export const handler: Handler = awslambda.streamifyResponse(
async (event: eventType, responseStream: Writable, _context: Context) => {
// BedrockRuntimeClientのインスタンスを作成し、リージョンを指定
const client = new BedrockRuntimeClient({ region: "us-east-1" });
// Lambda関数へ送信するデータペイロードを定義
const payload = {
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1000,
messages: [
{
role: "user",
content: [{ type: "text", text: event.prompt }],
},
],
};
// モデルに対するコマンドを生成し、リクエストの内容と型を指定
const command = new InvokeModelWithResponseStreamCommand({
contentType: "application/json",
body: JSON.stringify(payload),
modelId,
});
// APIコマンドを送信し、レスポンスを取得
const apiResponse = await client.send(command);
// レスポンスのボディを反復処理し、内容に基づいて条件分岐
if (apiResponse.body) {
for await (const item of apiResponse.body) {
if (item.chunk) {
// 受け取ったチャンクデータをJSONとして解析し、ストリームに書き込み
const chunk = JSON.parse(new TextDecoder().decode(item.chunk.bytes));
const chunk_type = chunk.type;
// コンテンツタイプが「content_block_delta」の場合、テキストをレスポンスストリームに書き込み
if (chunk_type === "content_block_delta") {
const text = chunk.delta.text;
responseStream.write(text);
}
} else if (item.internalServerException) {
throw item.internalServerException
} else if (item.modelStreamErrorException) {
throw item.modelStreamErrorException
} else if (item.throttlingException) {
throw item.throttlingException
} else if (item.validationException) {
throw item.validationException
}
}
}
// ストリームの終了を宣言
responseStream.end()
}
)
- amplify/backend.tsを編集する。
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { hello } from './function/hello/resource'
import { bedrock } from './function/bedrock/resource'
import * as iam from 'aws-cdk-lib/aws-iam';
const backend = defineBackend({
auth,
hello,
bedrock,●追加
});
//認証されたユーザーのIAMロールの取得
const authenticatedUserIamRole = backend.auth.resources.authenticatedUserIamRole;
●ここから追加
const bedrockStatement = new iam.PolicyStatement({
actions: ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
resources: ["arn:aws:bedrock:us-east-1::foundation-model/*"]
})
●ここまで追加
//Lambda関数への実行権限の付与
backend.hello.resources.lambda.grantInvoke(authenticatedUserIamRole);
backend.bedrock.resources.lambda.grantInvoke(authenticatedUserIamRole);●追加
backend.bedrock.resources.lambda.addToRolePolicy(bedrockStatement)●追加
//amplify_outputs.jsonにLambda関数名を出力する。
backend.addOutput({
custom: {
helloFunctionName: backend.hello.resources.lambda.functionName,
bedrockFunctionName: backend.bedrock.resources.lambda.functionName,●追加
},
});
- sandbox の進行状況を確認する。
✨ Total time: 0.27s
[Sandbox] Watching for file changes...
File written: amplify_outputs.json
- CloudFormation の進行を確認する。
- lambdaの生成を確認する。
frontend
途中で一時中断中