モノレポのセットアップ
タスク1: モノレポを作成する
Section titled “タスク1: モノレポを作成する”新しいモノレポを作成するには、任意のディレクトリ内で次のコマンドを実行してください:
npx create-nx-workspace@21.6.5 dungeon-adventure --pm=pnpm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 dungeon-adventure --pm=yarn --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 dungeon-adventure --pm=npm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsnpx create-nx-workspace@21.6.5 dungeon-adventure --pm=bun --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgentsこれによりdungeon-adventureディレクトリ内にNXモノレポがセットアップされます。VSCodeでディレクトリを開くと、次のファイル構造が表示されます:
Directory.nx/
- …
Directory.vscode/
- …
Directorynode_modules/
- …
Directorypackages/ サブプロジェクトが配置される場所
- …
- .gitignore
- .npmrc
- .prettierignore
- .prettierrc
- nx.json Nx CLIとモノレポのデフォルト設定
- package.json 全Node依存関係の定義
- pnpm-lock.yaml または bun.lock, yarn.lock, package-lock.json(パッケージマネージャー依存)
- pnpm-workspace.yaml(pnpm使用時)
- README.md
- tsconfig.base.json 全Nodeベースサブプロジェクトが継承
- tsconfig.json
- aws-nx-plugin.config.mts Nx Plugin for AWSの設定
これで@aws/nx-pluginを使用して様々なサブプロジェクトを作成できるようになりました。
タスク2: Game APIを作成する
Section titled “タスク2: Game APIを作成する”まず、Game APIを作成します。これを行うには、次の手順でGameApiというtRPC APIを作成します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#trpc-api - 必須パラメータを入力
- name: GameApi
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactiveyarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactivenpx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactivebunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-runファイルツリーに新しいファイルが表示されます。
ts#trpc-apiで更新されたファイル
以下はts#trpc-apiジェネレーターによって生成されたすべてのファイルのリストです。ファイルツリーで強調表示されている主要なファイルを確認します:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ アプリ固有CDKコンストラクト
Directoryapis/
- game-api.ts tRPC API作成用CDKコンストラクト
- index.ts
- …
- index.ts
Directorycore/ 汎用CDKコンストラクト
Directoryapi/
- rest-api.ts API Gateway Rest API用ベースコンストラクト
- trpc-utils.ts trpc API CDKコンストラクト用ユーティリティ
- utils.ts APIコンストラクト用ユーティリティ
- index.ts
- runtime-config.ts
- index.ts
- project.json
- …
Directorytypes/ 共有型定義
Directorysrc/
- index.ts
- runtime-config.ts CDKとウェブサイト間で使用されるインターフェース定義
- project.json
- …
Directorygame-api/ tRPC API
Directorysrc/
Directoryclient/ 機械間TS呼び出し用バニラクライアント
- index.ts
- sigv4.ts
Directorymiddleware/ Powertools計装
- error.ts
- index.ts
- logger.ts
- metrics.ts
- tracer.ts
Directoryschema/ APIの入出力定義
- echo.ts
Directoryprocedures/ APIプロシージャ/ルート実装
- echo.ts
- index.ts
- init.ts コンテキストとミドルウェア設定
- local-server.ts ローカルtRPCサーバー実行用
- router.ts 全プロシージャを定義するLambdaハンドラーエントリーポイント
- project.json
- …
- eslint.config.mjs
- vitest.workspace.ts
これらの主要ファイルを見てみましょう:
import { awsLambdaRequestHandler, CreateAWSLambdaContextOptions,} from '@trpc/server/adapters/aws-lambda';import { echo } from './procedures/echo.js';import { t } from './init.js';import { APIGatewayProxyEvent } from 'aws-lambda';
export const router = t.router;
export const appRouter = router({ echo,});
export const handler = awsLambdaRequestHandler({ router: appRouter, createContext: ( ctx: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>, ) => ctx, responseMeta: () => ({ headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', }, }),});
export type AppRouter = typeof appRouter;ルーターはtRPC APIのエントリーポイントであり、すべてのAPIメソッドを宣言する場所です。上記のように、echoメソッドがあり、その実装は./procedures/echo.tsファイルにあります。
import { publicProcedure } from '../init.js';import { EchoInputSchema, EchoOutputSchema,} from '../schema/echo.js';
export const echo = publicProcedure .input(EchoInputSchema) .output(EchoOutputSchema) .query((opts) => ({ result: opts.input.message }));このファイルはechoメソッドの実装であり、入力および出力データ構造を宣言することで厳密に型付けされています。
import { z } from 'zod';
export const EchoInputSchema = z.object({ message: z.string(),});
export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
export const EchoOutputSchema = z.object({ result: z.string(),});
export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;すべてのtRPCスキーマ定義はZodを使用して定義され、z.TypeOf構文を介してTypeScript型としてエクスポートされます。
import { Construct } from 'constructs';import * as url from 'url';import { Code, Runtime, Function, FunctionProps, Tracing,} from 'aws-cdk-lib/aws-lambda';import { AuthorizationType, Cors, LambdaIntegration,} from 'aws-cdk-lib/aws-apigateway';import { Duration, Stack } from 'aws-cdk-lib';import { PolicyDocument, PolicyStatement, Effect, AccountPrincipal, AnyPrincipal,} from 'aws-cdk-lib/aws-iam';import { IntegrationBuilder, RestApiIntegration,} from '../../core/api/utils.js';import { RestApi } from '../../core/api/rest-api.js';import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';import { AppRouter, appRouter } from ':dungeon-adventure/game-api';
// すべてのAPI操作名の文字列ユニオン型type Operations = Procedures<AppRouter>;
/** * GameApiコンストラクトを作成するためのプロパティ * * @template TIntegrations - 操作名からそれらの統合へのマップ */export interface GameApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { /** * 操作名からそれらのAPI Gateway統合へのマップ */ integrations: TIntegrations;}
/** * GameApi専用のAWS API Gateway REST APIを作成および設定するCDKコンストラクト * @template TIntegrations - 操作名からそれらの統合へのマップ */export class GameApi< TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> { /** * すべての操作のデフォルト統合を作成します。各操作を * 独自の個別のLambda関数として実装します。 * * @param scope - CDKコンストラクトスコープ * @returns デフォルトのLambda統合を持つIntegrationBuilder */ public static defaultIntegrations = (scope: Construct) => { return IntegrationBuilder.rest({ operations: routerToOperations(appRouter), defaultIntegrationOptions: { runtime: Runtime.NODEJS_LATEST, handler: 'index.handler', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/game-api/bundle', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, } satisfies FunctionProps, buildDefaultIntegration: (op, props: FunctionProps) => { const handler = new Function(scope, `GameApi${op}Handler`, props); return { handler, integration: new LambdaIntegration(handler) }; }, }); };
constructor( scope: Construct, id: string, props: GameApiProps<TIntegrations>, ) { super(scope, id, { apiName: 'GameApi', defaultMethodOptions: { authorizationType: AuthorizationType.IAM, }, defaultCorsPreflightOptions: { allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS, }, policy: new PolicyDocument({ statements: [ // ここでは、プロジェクトがデプロイされているアカウントからのすべてのAWS認証情報に // APIを呼び出す権限を付与します。 // 必要に応じて、より具体的なプリンシパル(ロールやユーザーなど)とリソース // (どのプリンシパルがどのAPIパスを呼び出せるかなど)を使用して、 // マシン間のきめ細かいアクセスをここで定義できます。 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), // ブラウザが認証されていないプリフライトリクエストを行えるようにOPTIONSを開放 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: routerToOperations(appRouter), ...props, }); }}これはGameApiを定義するCDKコンストラクトです。defaultIntegrationsメソッドを提供し、tRPC APIの各プロシージャに対してLambda関数を自動的に作成し、バンドルされたAPI実装を指します。これは、cdk synth時にバンドリングが発生しないことを意味します(NodeJsFunctionの使用とは対照的)。バックエンドプロジェクトのビルドターゲットの一部として既にバンドルしているためです。
タスク3: ストーリーエージェントを作成する
Section titled “タスク3: ストーリーエージェントを作成する”次にストーリーエージェントを作成しましょう。
ストーリーエージェント: Pythonプロジェクト
Section titled “ストーリーエージェント: Pythonプロジェクト”Pythonプロジェクトを作成するには:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - py#project - 必須パラメータを入力
- name: story
- クリック
Generate
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactiveyarn nx g @aws/nx-plugin:py#project --name=story --no-interactivenpx nx g @aws/nx-plugin:py#project --name=story --no-interactivebunx nx g @aws/nx-plugin:py#project --name=story --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#project --name=story --no-interactive --dry-runファイルツリーに新しいファイルが表示されます。
py#projectで更新されたファイル
py#projectは次のファイルを生成します:
Directory.venv/ モノレポ用単一仮想環境
- …
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ Pythonモジュール
- hello.py サンプルPythonファイル(無視します)
Directorytests/
- …
- .python-version
- pyproject.toml
- project.json
- .python-version 固定UVPythonバージョン
- pyproject.toml
- uv.lock
これにより、共有仮想環境を持つPythonプロジェクトとUV Workspaceが設定されました。
ストーリーエージェント: Strandsエージェント
Section titled “ストーリーエージェント: Strandsエージェント”py#strands-agentジェネレーターを使用してプロジェクトにStrandsエージェントを追加するには:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - py#strands-agent - 必須パラメータを入力
- project: story
- クリック
Generate
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactiveyarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactivenpx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactivebunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runyarn nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runnpx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runbunx nx g @aws/nx-plugin:py#strands-agent --project=story --no-interactive --dry-runファイルツリーに新しいファイルが表示されます。
py#strands-agentで更新されたファイル
py#strands-agentは次のファイルを生成します:
Directorypackages/
Directorystory/
Directorydungeon_adventure_story/ Pythonモジュール
Directoryagent/
- main.py Bedrock AgentCore Runtimeでのエージェントのエントリーポイント
- agent.py サンプルエージェントとツールを定義
- agentcore_mcp_client.py MCPサーバーとやり取りするクライアントを作成するためのユーティリティ
- Dockerfile AgentCore Runtimeへのデプロイ用Dockerイメージを定義
Directorycommon/constructs/
Directorysrc
Directorycore/agent-core/
- runtime.ts AgentCore Runtimeへのデプロイ用汎用コンストラクト
Directoryapp/agents/story-agent/
- story-agent.ts ストーリーエージェントをAgentCore Runtimeにデプロイするためのコンストラクト
いくつかのファイルを詳しく見てみましょう:
from contextlib import contextmanager
from strands import Agent, toolfrom strands_tools import current_time
# カスタムツールを定義@tooldef add(a: int, b: int) -> int: return a + b
@contextmanagerdef get_agent(session_id: str): yield Agent( system_prompt="""You are an addition wizard.Use the 'add' tool for addition tasks.Refer to tools as your 'spellbook'.""", tools=[add, current_time], )これは、サンプルStrandsエージェントを作成し、加算ツールを定義します。
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from .agent import get_agent
app = BedrockAgentCoreApp()
@app.entrypointasync def invoke(payload, context): """エージェント呼び出しのハンドラー""" prompt = payload.get( "prompt", "No prompt found in input, please guide the user " "to create a json payload with prompt key" )
with get_agent(session_id=context.session_id) as agent: stream = agent.stream_async(prompt) async for event in stream: print(event) yield (event)
if __name__ == "__main__": app.run()これは、Amazon Bedrock AgentCore SDKを使用して設定されたエージェントのエントリーポイントです。ストリーミングのStrandsサポートを使用し、発生したイベントをクライアントにストリーミングで返します。
import { Lazy, Names } from 'aws-cdk-lib';import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets';import { Construct } from 'constructs';import { execSync } from 'child_process';import * as path from 'path';import * as url from 'url';import { AgentCoreRuntime, AgentCoreRuntimeProps,} from '../../../core/agent-core/runtime.js';
export type StoryAgentProps = Omit< AgentCoreRuntimeProps, 'runtimeName' | 'serverProtocol' | 'containerUri'>;
export class StoryAgent extends Construct { public readonly dockerImage: DockerImageAsset; public readonly agentCoreRuntime: AgentCoreRuntime;
constructor(scope: Construct, id: string, props?: StoryAgentProps) { super(scope, id);
this.dockerImage = new DockerImageAsset(this, 'DockerImage', { platform: Platform.LINUX_ARM64, directory: path.dirname(url.fileURLToPath(new URL(import.meta.url))), extraHash: execSync( `docker inspect dungeon-adventure-story-agent:latest --format '{{.Id}}'`, { encoding: 'utf-8' }, ).trim(), });
this.agentCoreRuntime = new AgentCoreRuntime(this, 'StoryAgent', { runtimeName: Lazy.string({ produce: () => Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }), }), serverProtocol: 'HTTP', containerUri: this.dockerImage.imageUri, ...props, }); }}これは、エージェントDockerイメージをECRにアップロードし、AgentCore Runtimeを使用してホストするCDK DockerImageAssetを設定します。
追加のDockerfileに気付くかもしれませんが、これはstoryプロジェクトからDockerイメージを参照し、Dockerfileとエージェントソースコードを同じ場所に配置できるようにします。
タスク4: インベントリツールをセットアップする
Section titled “タスク4: インベントリツールをセットアップする”インベントリ: TypeScriptプロジェクト
Section titled “インベントリ: TypeScriptプロジェクト”ストーリーエージェントがプレイヤーのインベントリを管理するためのツールを提供するMCPサーバーを作成しましょう。
まず、TypeScriptプロジェクトを作成します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#project - 必須パラメータを入力
- name: inventory
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactiveyarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactivenpx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactivebunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#project --name=inventory --no-interactive --dry-runこれにより、空のTypeScriptプロジェクトが作成されます。
ts#projectで更新されたファイル
ts#projectジェネレーターは次のファイルを生成します。
Directorypackages/
Directoryinventory/
Directorysrc/
- index.ts サンプル関数を含むエントリーポイント
- project.json プロジェクト設定
- eslint.config.mjs リント設定
- vite.config.ts テスト設定
- tsconfig.json プロジェクトのベースTypeScript設定
- tsconfig.lib.json コンパイルとバンドル用のプロジェクトのTypeScript設定
- tsconfig.spec.json テスト用のTypeScript設定
- tsconfig.base.json 他のプロジェクトがこれを参照するためのエイリアスを設定するように更新
インベントリ: MCPサーバー
Section titled “インベントリ: MCPサーバー”次に、TypeScriptプロジェクトにMCPサーバーを追加します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#mcp-server - 必須パラメータを入力
- project: inventory
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactiveyarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactivenpx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactivebunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#mcp-server --project=inventory --no-interactive --dry-runこれによりMCPサーバーが追加されます。
ts#mcp-serverで更新されたファイル
ts#mcp-serverジェネレーターは次のファイルを生成します。
Directorypackages/
Directoryinventory/
Directorysrc/mcp-server/
- server.ts MCPサーバーを作成
Directorytools/
- add.ts サンプルツール
Directoryresources/
- sample-guidance.ts サンプルリソース
- stdio.ts STDIOトランスポート用MCPのエントリーポイント
- http.ts ストリーミングHTTPトランスポート用MCPのエントリーポイント
- Dockerfile AgentCore Runtime用のイメージをビルド
- rolldown.config.ts AgentCoreへのデプロイ用にMCPサーバーをバンドルするための設定
Directorycommon/constructs/
Directorysrc
Directoryapp/mcp-servers/inventory-mcp-server/
- inventory-mcp-server.ts インベントリMCPサーバーをAgentCore Runtimeにデプロイするためのコンストラクト
タスク5: ユーザーインターフェース(UI)を作成する
Section titled “タスク5: ユーザーインターフェース(UI)を作成する”このタスクでは、ゲームとやり取りできるUIを作成します。
ゲームUI: ウェブサイト
Section titled “ゲームUI: ウェブサイト”UIを作成するには、次の手順でGameUIというウェブサイトを作成します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#react-website - 必須パラメータを入力
- name: GameUI
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactiveyarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactivenpx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactivebunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-runファイルツリーに新しいファイルが表示されます。
ts#react-websiteで更新されたファイル
ts#react-websiteは次のファイルを生成します。ファイルツリーで強調表示されている主要なファイルを確認しましょう:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ アプリ固有CDKコンストラクト
Directorystatic-websites/
- game-ui.ts Game UIを作成するためのCDKコンストラクト
Directorycore/
- static-website.ts 汎用静的ウェブサイトコンストラクト
Directorygame-ui/
Directorypublic/
- …
Directorysrc/
Directorycomponents/
DirectoryAppLayout/
- index.ts 全体的なページレイアウト: ヘッダー、フッター、サイドバーなど
- navitems.ts サイドバーナビゲーションアイテム
Directoryhooks/
- useAppLayout.tsx 通知、ページスタイルなどを動的に設定できる
Directoryroutes/ @tanstack/react-routerファイルベースルート
- index.tsx ルート’/‘ページは’/welcome’にリダイレクト
- __root.tsx すべてのページがこのコンポーネントをベースとして使用
Directorywelcome/
- index.tsx
- config.ts
- main.tsx Reactエントリーポイント
- routeTree.gen.ts これは@tanstack/react-routerによって自動的に更新されます
- styles.css
- index.html
- project.json
- vite.config.ts
- …
import * as url from 'url';import { Construct } from 'constructs';import { StaticWebsite } from '../../core/index.js';
export class GameUI extends StaticWebsite { constructor(scope: Construct, id: string) { super(scope, id, { websiteName: 'GameUI', websiteFilePath: url.fileURLToPath( new URL( '../../../../../../dist/packages/game-ui/bundle', import.meta.url, ), ), }); }}これはGameUIを定義するCDKコンストラクトです。ViteベースUIの生成されたバンドルへのファイルパスが既に設定されています。これは、build時に、game-uiプロジェクトのビルドターゲット内でバンドリングが発生し、その出力がここで使用されることを意味します。
import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';
import '@cloudscape-design/global-styles/index.css';
const router = createRouter({ routeTree });
// 型安全性のためにルーターインスタンスを登録declare module '@tanstack/react-router' { interface Register { router: typeof router; }}
const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RouterProvider router={router} /> </I18nProvider> </React.StrictMode>, );これはReactがマウントされるエントリーポイントです。示されているように、最初はfile-based-routing設定で@tanstack/react-routerを設定するだけです。開発サーバーが実行されている限り、routesフォルダ内にファイルを作成すると、@tanstack/react-routerがボイラープレートファイルのセットアップを作成し、routeTree.gen.tsファイルを更新します。このファイルは、すべてのルートを型安全な方法で維持します。つまり、<Link>を使用すると、toオプションには有効なルートのみが表示されます。
詳細については、@tanstack/react-routerドキュメントを参照してください。
import { ContentLayout, Header, SpaceBetween, Container,} from '@cloudscape-design/components';import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/welcome/')({ component: RouteComponent,});
function RouteComponent() { return ( <ContentLayout header={<Header>Welcome</Header>}> <SpaceBetween size="l"> <Container>Welcome to your new Cloudscape website!</Container> </SpaceBetween> </ContentLayout> );}/welcomeルートにナビゲートすると、コンポーネントがレンダリングされます。@tanstack/react-routerは、このファイルを作成/移動するたびに(開発サーバーが実行されている限り)Routeを管理します。
ゲームUI: 認証
Section titled “ゲームUI: 認証”次の手順を使用して、Amazon Cognito経由の認証アクセスを要求するようにGame UIを設定しましょう:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#react-website#auth - 必須パラメータを入力
- cognitoDomain: game-ui
- project: @dungeon-adventure/game-ui
- allowSignup: true
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactiveyarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivenpx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactivebunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-runファイルツリーに新しいファイルが表示/変更されます。
ts#react-website#authで更新されたファイル
ts#react-website#authジェネレーターはこれらのファイルを更新/生成します。ファイルツリーで強調表示されている主要なファイルを確認しましょう:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
- user-identity.ts ユーザー/アイデンティティプールを作成するためのCDKコンストラクト
Directorytypes/
Directorysrc/
- runtime-config.ts cognitoPropsを追加するように更新
Directorygame-ui/
Directorysrc/
Directorycomponents/
DirectoryAppLayout/
- index.tsx ログインユーザー/ログアウトをヘッダーに追加
DirectoryCognitoAuth/
- index.ts Cognitoへのログインを管理
DirectoryRuntimeConfig/
- index.tsx
runtime-config.jsonを取得し、コンテキスト経由で子に提供
- index.tsx
Directoryhooks/
- useRuntimeConfig.tsx
- main.tsx Cognitoを追加するように更新
import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// 型安全性のためにルーターインスタンスを登録declare module '@tanstack/react-router' { interface Register { router: typeof router; }}const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RuntimeConfigProvider> <CognitoAuth> <RouterProvider router={router} /> </CognitoAuth> </RuntimeConfigProvider> </I18nProvider> </React.StrictMode>, );RuntimeConfigProviderとCognitoAuthコンポーネントがAST変換を介してmain.tsxファイルに追加されました。これにより、CognitoAuthコンポーネントは、正しい宛先へのバックエンド呼び出しを行うために必要なCognito接続設定を含むruntime-config.jsonを取得することで、Amazon Cognitoで認証できるようになります。
ゲームUI: Game APIに接続する
Section titled “ゲームUI: Game APIに接続する”以前に作成したGame APIに接続するようにGame UIを設定しましょう。
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - api-connection - 必須パラメータを入力
- sourceProject: @dungeon-adventure/game-ui
- targetProject: @dungeon-adventure/game-api
- クリック
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactiveyarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivenpx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactivebunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runyarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runnpx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runbunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-runファイルツリーに新しいファイルが表示/変更されます。
UI → tRPC api-connectionで更新されたファイル
api-connectionジェネレーターはこれらのファイルを生成/更新します。ファイルツリーで強調表示されている主要なファイルを確認しましょう:
Directorypackages/
Directorygame-ui/
Directorysrc/
Directorycomponents/
- GameApiClientProvider.tsx GameAPIクライアントをセットアップ
Directoryhooks/
- useGameApi.tsx GameApiを呼び出すためのフック
- main.tsx trpcクライアントプロバイダーを注入
- package.json
import { GameApiTRCPContext } from '../components/GameApiClientProvider';
export const useGameApi = GameApiTRCPContext.useTRPC;このフックは、tRPCの最新のReact Query統合を使用しており、ユーザーは追加の抽象化レイヤーなしで@tanstack/react-queryと直接やり取りできます。tRPC APIを呼び出す方法の例については、tRPCフックの使用ガイドを参照してください。
import GameApiClientProvider from './components/GameApiClientProvider';import QueryClientProvider from './components/QueryClientProvider';import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// 型安全性のためにルーターインスタンスを登録declare module '@tanstack/react-router' { interface Register { router: typeof router; }}const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RuntimeConfigProvider> <CognitoAuth> <QueryClientProvider> <GameApiClientProvider> <RouterProvider router={router} /> </GameApiClientProvider> </QueryClientProvider> </CognitoAuth> </RuntimeConfigProvider> </I18nProvider> </React.StrictMode>, );main.tsxファイルがAST変換を介して更新され、tRPCプロバイダーが注入されました。
ゲームUI: インフラストラクチャー
Section titled “ゲームUI: インフラストラクチャー”CDKインフラストラクチャーの最終サブプロジェクトを作成しましょう。
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - ts#infra - 必須パラメータを入力
- name: infra
- クリック
Generate
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactiveyarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactivenpx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactivebunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runyarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runnpx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runbunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-runファイルツリーに新しいファイルが表示/変更されます。
ts#infraで更新されたファイル
ts#infraジェネレーターはこれらを生成/更新します。ファイルツリーで強調表示されている主要なファイルを確認しましょう:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
- checkov.ts
- index.ts
Directoryinfra
Directorysrc/
Directorystages/
- application-stage.ts CDKスタックがここで定義されます
Directorystacks/
- application-stack.ts CDKリソースがここで定義されます
- index.ts
- main.ts すべてのステージを定義するエントリーポイント
- cdk.json
- project.json
- …
- package.json
- tsconfig.json 参照を追加
- tsconfig.base.json エイリアスを追加
import { ApplicationStage } from './stacks/application-stage.js';import { App } from ':dungeon-adventure/common-constructs';
const app = new App();
// これを使用して独自のサンドボックス環境をデプロイします(CLI認証情報を想定)new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, },});
app.synth();これはCDKアプリケーションのエントリーポイントです。
import * as cdk from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props);
// スタックを定義するコードはここに記述します }}ダンジョンアドベンチャーゲームを構築するためにCDKコンストラクトをインスタンス化しましょう。
タスク6: インフラストラクチャーを更新する
Section titled “タスク6: インフラストラクチャーを更新する”生成されたコンストラクトの一部をインスタンス化するために、packages/infra/src/stacks/application-stack.tsを更新しましょう:
import { GameApi, GameUI, InventoryMcpServer, RuntimeConfig, StoryAgent, UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
const userIdentity = new UserIdentity(this, 'UserIdentity');
const gameApi = new GameApi(this, 'GameApi', { integrations: GameApi.defaultIntegrations(this).build(), });
const { userPool, userPoolClient } = userIdentity;
const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
// Use Cognito for user authentication with the agent const storyAgent = new StoryAgent(this, 'StoryAgent', { authorizerConfiguration: { customJwtAuthorizer: { discoveryUrl: `https://cognito-idp.${Stack.of(userPool).region}.amazonaws.com/${userPool.userPoolId}/.well-known/openid-configuration`, allowedAudience: [userPoolClient.userPoolClientId], }, }, environment: { INVENTORY_MCP_ARN: mcpServer.agentCoreRuntime.arn, }, }); // Add the Story Agent ARN to runtime-config.json so it can be referenced by the website RuntimeConfig.ensure(this).config.agentArn = storyAgent.agentCoreRuntime.arn;
new CfnOutput(this, 'StoryAgentArn', { value: storyAgent.agentCoreRuntime.arn, }); new CfnOutput(this, 'InventoryMcpArn', { value: mcpServer.agentCoreRuntime.arn, });
// Grant the agent permissions to invoke our mcp server mcpServer.agentCoreRuntime.grantInvoke(storyAgent.agentCoreRuntime);
// Grant the authenticated role access to invoke the api gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
// Ensure this is instantiated last so our runtime-config.json can be automatically configured new GameUI(this, 'GameUI'); }}import { Stack, StackProps } from 'aws-cdk-lib';import { GameApi, GameUI, InventoryMcpServer, RuntimeConfig, StoryAgent, UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps, CfnOutput } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
// The code that defines your stack goes here const userIdentity = new UserIdentity(this, 'UserIdentity');
const gameApi = new GameApi(this, 'GameApi', { integrations: GameApi.defaultIntegrations(this).build(), });
const { userPool, userPoolClient } = userIdentity;
const mcpServer = new InventoryMcpServer(this, 'InventoryMcpServer');
// Use Cognito for user authentication with the agent const storyAgent = new StoryAgent(this, 'StoryAgent', { authorizerConfiguration: { customJwtAuthorizer: { discoveryUrl: `https://cognito-idp.${Stack.of(userPool).region}.amazonaws.com/${userPool.userPoolId}/.well-known/openid-configuration`, allowedAudience: [userPoolClient.userPoolClientId], }, }, environment: { INVENTORY_MCP_ARN: mcpServer.agentCoreRuntime.arn, }, }); // Add the Story Agent ARN to runtime-config.json so it can be referenced by the website RuntimeConfig.ensure(this).config.agentArn = storyAgent.agentCoreRuntime.arn;
new CfnOutput(this, 'StoryAgentArn', { value: storyAgent.agentCoreRuntime.arn, }); new CfnOutput(this, 'InventoryMcpArn', { value: mcpServer.agentCoreRuntime.arn, });
// Grant the agent permissions to invoke our mcp server mcpServer.agentCoreRuntime.grantInvoke(storyAgent.agentCoreRuntime);
// Grant the authenticated role access to invoke the api gameApi.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
// Ensure this is instantiated last so our runtime-config.json can be automatically configured new GameUI(this, 'GameUI'); }}タスク7: コードをビルドする
Section titled “タスク7: コードをビルドする”Nxコマンド
単一 vs 複数ターゲット
Section titled “単一 vs 複数ターゲット”run-manyコマンドは、リストされた複数のサブプロジェクトでターゲットを実行します(--allはすべてをターゲットにします)。これにより、依存関係が正しい順序で実行されます。
プロジェクトで直接ターゲットを実行することで、単一のプロジェクトターゲットのビルド(または他のタスク)をトリガーすることもできます。たとえば、@dungeon-adventure/infraプロジェクトをビルドするには、次のコマンドを実行します:
pnpm nx run @dungeon-adventure/infra:buildyarn nx run @dungeon-adventure/infra:buildnpx nx run @dungeon-adventure/infra:buildbunx nx run @dungeon-adventure/infra:buildスコープを省略して、Nxの省略構文を使用することもできます:
pnpm nx build infrayarn nx build infranpx nx build infrabunx nx build infra依存関係の可視化
Section titled “依存関係の可視化”依存関係を可視化するには、次を実行します:
pnpm nx graphyarn nx graphnpx nx graphbunx nx graph
Nxはキャッシュに依存しているため、開発を高速化するために以前のビルドの成果物を再利用できます。これを正しく機能させるにはいくつかの設定が必要であり、キャッシュを使用せずにビルドを実行したい場合があります。そのためには、コマンドに--skip-nx-cache引数を追加するだけです。例:
pnpm nx run @dungeon-adventure/infra:build --skip-nx-cacheyarn nx run @dungeon-adventure/infra:build --skip-nx-cachenpx nx run @dungeon-adventure/infra:build --skip-nx-cachebunx nx run @dungeon-adventure/infra:build --skip-nx-cache何らかの理由でキャッシュ(.nxフォルダに保存)をクリアしたい場合は、次のコマンドを実行できます:
pnpm nx resetyarn nx resetnpx nx resetbunx nx resetコマンドラインを使用して、次を実行します:
pnpm nx run-many --target build --allyarn nx run-many --target build --allnpx nx run-many --target build --allbunx nx run-many --target build --all次のプロンプトが表示されます:
NX The workspace is out of sync
[@nx/js:typescript-sync]: Some TypeScript configuration files are missing project references to the projects they depend on or contain outdated project references.
This will result in an error in CI.
? Would you like to sync the identified changes to get your workspace up to date? …Yes, sync the changes and run the tasksNo, run the tasks without syncing the changesこのメッセージは、NXが自動的に更新できるファイルを検出したことを示しています。この場合、参照プロジェクトにTypeScript参照が設定されていないtsconfig.jsonファイルを指しています。
Yes, sync the changes and run the tasksオプションを選択して続行します。同期ジェネレーターが不足しているTypeScript参照を自動的に追加するため、IDEに関連するすべてのインポートエラーが自動的に解決されることに気付くはずです!
すべてのビルド成果物は、モノレポのルートにあるdist/フォルダ内で利用できるようになりました。これは、@aws/nx-pluginによって生成されたプロジェクトを使用する場合の標準的な慣行であり、生成されたファイルでファイルツリーが汚染されることはありません。ファイルをクリーンにしたい場合は、生成されたファイルがファイルツリー全体に散らばることを心配せずにdist/フォルダを削除してください。
おめでとうございます!AIダンジョンアドベンチャーゲームのコアの実装を開始するために必要なすべてのサブプロジェクトを作成しました。🎉🎉🎉