AIダンジョンゲーム
モジュール1: モノレポのセットアップ
Section titled “モジュール1: モノレポのセットアップ”まず新しいモノレポを作成します。任意のディレクトリで次のコマンドを実行してください:
npx create-nx-workspace@~21.4.1 dungeon-adventure --pm=pnpm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip
npx create-nx-workspace@~21.4.1 dungeon-adventure --pm=yarn --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip
npx create-nx-workspace@~21.4.1 dungeon-adventure --pm=npm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip
npx create-nx-workspace@~21.4.1 dungeon-adventure --pm=bun --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip
これにより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
を使用してさまざまなサブプロジェクトを作成する準備が整いました。
ゲームAPI
Section titled “ゲーム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-interactive
yarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
npx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
bunx 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-run
yarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
bunx 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/ 機械間通信用バニラクライアント
- 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呼び出しを許可 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), // ブラウザのプリフライトリクエストを許可 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
Section titled “ストーリーAPI”次にStory APIを作成します。以下の手順でStoryApi
というFast APIを作成します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)
"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - py#fast-api
- 必須パラメータを入力
- name: StoryApi
- moduleName: story_api
- クリック
Generate
pnpm nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
yarn nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
npx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
bunx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
npx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
ファイルツリーに新しいファイルが生成されます。
py#fast-apiで更新されたファイル
py#fast-api
ジェネレーターで生成された主要ファイル:
Directory.venv/ モノレポ用仮想環境
- …
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directoryapp/ アプリ固有CDKコンストラクト
Directoryapis/
- story-api.ts Fast API用CDKコンストラクト
- index.ts 更新して新しいstory-apiをエクスポート
- project.json story_apiのビルド依存関係を追加
Directorytypes/ 共有型
Directorysrc/
- runtime-config.ts StoryApiを追加
Directorystory_api/
Directorystory_api/ Pythonモジュール
- init.py Powertools、FastAPI、ミドルウェアのセットアップ
- main.py すべてのルートを含むLambdaエントリーポイント
Directorytests/
- …
- .python-version
- project.json
- pyproject.toml
- project.json
- .python-version UV Pythonバージョン固定
- pyproject.toml
- uv.lock
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 { OPERATION_DETAILS, Operations,} from '../../generated/story-api/metadata.gen.js';
/** * StoryApiコンストラクト作成用プロパティ * * @template TIntegrations - 操作名とインテグレーションのマップ */export interface StoryApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { /** * 操作名とAPI Gatewayインテグレーションのマップ */ integrations: TIntegrations;}
/** * StoryApi用AWS API Gateway REST APIを構成するCDKコンストラクト * @template TIntegrations - 操作名とインテグレーションのマップ */export class StoryApi< 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: OPERATION_DETAILS, defaultIntegrationOptions: { runtime: Runtime.PYTHON_3_12, handler: 'story_api.main.handler', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/story_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, `StoryApi${op}Handler`, props); return { handler, integration: new LambdaIntegration(handler) }; }, }); };
constructor( scope: Construct, id: string, props: StoryApiProps<TIntegrations>, ) { super(scope, id, { apiName: 'StoryApi', defaultMethodOptions: { authorizationType: AuthorizationType.IAM, }, defaultCorsPreflightOptions: { allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS, }, policy: new PolicyDocument({ statements: [ // デプロイ先アカウントのAWS認証情報にAPI呼び出しを許可 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), // ブラウザのプリフライトリクエストを許可 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: OPERATION_DETAILS, ...props, }); }}
StoryApiを定義するCDKコンストラクトです。defaultIntegrations
メソッドにより、FastAPIの各操作用に個別のLambda関数が自動生成されます。
from .init import app, lambda_handler, tracer
handler = lambda_handler
@app.get("/")@tracer.capture_methoddef read_root(): return {"Hello": "World"}
APIメソッドを定義する場所です。Pydanticを使用して入出力を型安全に定義できます。
ゲーム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-interactive
yarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
npx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
bunx 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-run
yarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
bunx 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のバンドルパスが設定されています。
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のマウントポイントです。ファイルベースルーティングが設定されています。
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
ルート用コンポーネントです。後続のセクションで説明されます。
ゲームUI: 認証
Section titled “ゲームUI: 認証”Amazon Cognitoによる認証を設定します:
- インストール 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-interactive
yarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
npx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
bunx 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-run
yarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
bunx 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 ユーザー/IDプール作成用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
コンポーネントが追加され、Cognito認証が有効になります。
ゲームUI: Story API接続
Section titled “ゲームUI: Story API接続”Game UIをStory APIに接続します:
- インストール Nx Console VSCode Plugin まだインストールしていない場合
- VSCodeでNxコンソールを開く
- クリック
Generate (UI)
"Common Nx Commands"セクションで - 検索
@aws/nx-plugin - api-connection
- 必須パラメータを入力
- sourceProject: @dungeon-adventure/game-ui
- targetProject: dungeon_adventure.story_api
- クリック
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
ファイルツリーが更新されます。
UI -> FastAPI接続で更新されたファイル
api-connection
ジェネレーターで更新された主要ファイル:
Directorypackages/
Directorygame-ui/
Directorysrc/
Directoryhooks/
- useSigV4.tsx StoryApiリクエスト署名用
- useStoryApiClient.tsx StoryApiクライアント構築用フック
- useStoryApi.tsx TanStack Queryを使用したStoryApi操作用フック
Directorycomponents/
- QueryClientProvider.tsx TanStack Queryクライアントプロバイダー
- StoryApiProvider.tsx StoryApi TanStack Queryフック用プロバイダー
- main.tsx QueryClientProviderとStoryApiProviderを追加
- .gitignore 生成クライアントファイルを無視
- project.json OpenAPIフック生成ターゲットを追加
- …
Directorystory_api/
Directoryscripts/
- generate_open_api.py
- project.json openapi.json生成を追加
import { StoryApi } from '../generated/story-api/client.gen';import { useSigV4 } from './useSigV4';import { useRuntimeConfig } from './useRuntimeConfig';import { useMemo } from 'react';
export const useStoryApi = (): StoryApi => { const runtimeConfig = useRuntimeConfig(); const apiUrl = runtimeConfig.apis.StoryApi; const sigv4Client = useSigV4(); return useMemo( () => new StoryApi({ url: apiUrl, fetch: sigv4Client, }), [apiUrl, sigv4Client], );};
StoryApiへの認証済みリクエスト用フックです。ビルド時に生成されるクライアントを使用します。
import { createContext, FC, PropsWithChildren, useMemo } from 'react';import { useStoryApiClient } from '../hooks/useStoryApiClient';import { StoryApiOptionsProxy } from '../generated/story-api/options-proxy.gen';
export const StoryApiContext = createContext<StoryApiOptionsProxy | undefined>( undefined,);
export const StoryApiProvider: FC<PropsWithChildren> = ({ children }) => { const client = useStoryApiClient(); const optionsProxy = useMemo( () => new StoryApiOptionsProxy({ client }), [client], );
return ( <StoryApiContext.Provider value={optionsProxy}> {children} </StoryApiContext.Provider> );};
export default StoryApiProvider;
StoryApiクライアントを提供するプロバイダーコンポーネントです。
ゲームUI: Game API接続
Section titled “ゲームUI: Game API接続”Game UIをGame APIに接続します:
- インストール 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-interactive
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
bunx 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-run
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
bunx 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
ジェネレーターで更新された主要ファイル:
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統合を使用するフックです。
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>, );
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-interactive
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
変更されるファイルを確認するためにドライランを実行することもできます
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
ファイルツリーが更新されます。
ts#infraで更新されたファイル
ts#infra
ジェネレーターで更新された主要ファイル:
Directorypackages/
Directorycommon/
Directoryconstructs/
Directorysrc/
Directorycore/
Directorycfn-guard-rules/
- *.guard
- cfn-guard.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, CfnGuardValidator, RuleSet,} from ':dungeon-adventure/common-constructs';
const app = new App({ policyValidationBeta1: [new CfnGuardValidator(RuleSet.AWS_PROTOTYPING)],});
// サンドボックス環境デプロイ用(CLI認証情報を使用)new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, },});
app.synth();
CDKアプリケーションのエントリーポイントです。cfn-guard
を使用したインフラ検証が設定されています。
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コンストラクトを配置する場所です。
インフラストラクチャの更新
Section titled “インフラストラクチャの更新”packages/infra/src/stacks/application-stack.ts
を更新し、生成済みコンストラクトを初期化します:
import { GameApi, GameUI, StoryApi, UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps } 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 storyApi = new StoryApi(this, 'StoryApi', { integrations: StoryApi.defaultIntegrations(this).build(), });
// grant our authenticated role access to invoke our APIs [storyApi, gameApi].forEach((api) => api.grantInvokeAccess(userIdentity.identityPool.authenticatedRole), );
// Ensure this is instantiated last so our runtime-config.json can be automatically configured new GameUI(this, 'GameUI'); }}
各API操作に個別のLambda関数をマッピングするデフォルトインテグレーションを設定しています。
コードのビルド
Section titled “コードのビルド”Nxコマンド
単一 vs 複数ターゲット
Section titled “単一 vs 複数ターゲット”run-many
コマンドは複数サブプロジェクトでターゲットを実行します(--all
で全対象)。依存関係を正しい順序で実行します。
単一プロジェクトのビルドは次のように実行:
pnpm nx run @dungeon-adventure/infra:build
yarn nx run @dungeon-adventure/infra:build
npx nx run @dungeon-adventure/infra:build
bunx nx run @dungeon-adventure/infra:build
依存関係の可視化
Section titled “依存関係の可視化”依存関係グラフを表示:
pnpm nx graph
yarn nx graph
npx nx graph
bunx nx graph

Nxはキャッシュを使用してビルドを高速化します。キャッシュを無効にするには--skip-nx-cache
を追加:
pnpm nx run @dungeon-adventure/infra:build --skip-nx-cache
yarn nx run @dungeon-adventure/infra:build --skip-nx-cache
npx nx run @dungeon-adventure/infra:build --skip-nx-cache
bunx nx run @dungeon-adventure/infra:build --skip-nx-cache
キャッシュクリア:
pnpm nx reset
yarn nx reset
npx nx reset
bunx nx reset
pnpm nx run-many --target build --all
yarn nx run-many --target build --all
npx nx run-many --target build --all
bunx nx run-many --target build --all
次のプロンプトが表示されます:
NX ワークスペースが同期されていません
[@nx/js:typescript-sync]: TypeScript設定ファイルにプロジェクト参照が不足しているか、古い参照が含まれています。
CIでエラーが発生します。
? 変更を同期してワークスペースを最新状態にしますか? …はい、変更を同期してタスクを実行いいえ、同期せずにタスクを実行
「はい」を選択するとTypeScript参照が自動追加され、IDEエラーが解消されます。
ビルド成果物はモノレポルートのdist/
フォルダに生成されます。クリーンアップ時はdist/
を削除します。
おめでとうございます!ダンジョンアドベンチャーゲームのコア実装に必要なすべてのサブプロジェクトを作成しました。🎉🎉🎉