よくある問題
このページでは、MBC CQRS Serverlessアプリケーション開発時によくある問題とその解決策を紹介します。
インストールとセットアップ
npm installがピア依存関係エラーで失敗する
症状: npm installがピア依存関係の警告またはエラーで失敗する。
解決策:
# --legacy-peer-depsフラグを使用
npm install --legacy-peer-deps
# または、npmを最新バージョンに更新
npm install -g npm@latest
npm installがnode-wafエラーで失敗する
症状: npm installが node-waf: command not found エラーで失敗する(通常はzlibパッケージが原因)。
npm error path node_modules/zlib
npm error command sh -c node-waf clean || true; node-waf configure build
npm error sh: node-waf: command not found
原因: 一部のレガシーなserverless-offlineプラグインが、廃止されたnode-wafビルドツールを使用するパッケージに依存しています。
解決策:
# インストール時にビルドスクリプトをスキップ
npm install --legacy-peer-deps --ignore-scripts
# その後、postinstallスクリプトを手動で実行
npx prisma generate
CLIコマンドが見つからない
症状: mbc-cqrs コマンドが認識されない。
解決策:
# Install globally (グローバルにインストール)
npm install -g @mbc-cqrs-serverless/cli
# Or use npx (npxで使用)
npx @mbc-cqrs-serverless/cli new my-app
Dockerサービスが起動しない
症状: docker-compose upが失敗するか、サービスが起動しない。
解決策:
# Check Docker is running (Dockerが実行中か確認)
docker info
# Clean up and restart (クリーンアップして再起動)
docker-compose -f infra-local/docker-compose.yml down -v
docker-compose -f infra-local/docker-compose.yml up -d
# Check logs for specific service (特定のサービスのログを確認)
docker-compose -f infra-local/docker-compose.yml logs dynamodb-local
データベースの問題
DynamoDB接続が拒否される
症状: DynamoDB Localに接続できない。
解決策:
- DynamoDB Localが実行中であることを確認:
docker ps | grep dynamodb
- 設定でエンドポイントURLを確認:
// Should be http://localhost:8000 for local development (ローカル開発では http://localhost:8000 を使用)
dynamodbEndpoint: 'http://localhost:8000'
- テーブルが存在することを確認:
aws dynamodb list-tables --endpoint-url http://localhost:8000
Prismaマイグレーションエラー
症状: Prisma migrateが接続エラーで失敗する。
解決策:
- データベースコンテナが起動していることを確認します(スキャフォールドされたプロジェクトはデフォルトでMySQLを使用します):
docker ps | grep mysql
- .envのDATABASE_URLがデータベースと一致していることを確認します。スキャフォールドされたプロジェクトのデフォルトはMySQLです:
DATABASE_URL="mysql://root:RootCqrs@localhost:3306/cqrs?schema=public&connection_limit=1"
- リセットしてマイグレーションを再実行:
npx prisma migrate reset
npx prisma migrate dev
Master APIが500 Internal Server Errorを返す
症状: Master APIエンドポイント(/api/master-setting/list、/api/master-data/list)が500 Internal Server Errorを返す。
原因: MasterモジュールはDynamoDBテーブルとRDSテーブルの両方を必要とします。RDSにmastersテーブルが存在しない場合、APIは500エラーで失敗します。
解決策:
- RDSに
mastersテーブルが存在することを確認:
# MySQLの場合
docker exec mysql mysql -u root -proot mydb -e "SHOW TABLES LIKE 'masters';"
# PostgreSQLの場合
docker exec postgres psql -U postgres -d mydb -c "\dt masters"
- テーブルがない場合は、Prismaスキーマに追加:
model Master {
pk String @db.VarChar(256)
sk String @db.VarChar(512)
id String @id @db.VarChar(256)
name String @db.VarChar(256)
code String @db.VarChar(256)
version Int @default(0)
tenantCode String @map("tenant_code") @db.VarChar(64)
type String @db.VarChar(256)
attributes Json?
isDeleted Boolean @default(false) @map("is_deleted")
createdAt DateTime @default(now()) @map("created_at")
createdBy String @default("system") @map("created_by") @db.VarChar(256)
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
updatedBy String @default("system") @map("updated_by") @db.VarChar(256)
@@unique([pk, sk])
@@index([tenantCode])
@@map("masters")
}
- Prismaマイグレーションを実行:
npx prisma migrate dev --name add_master_table
- DynamoDBテーブルも存在することを確認:
aws dynamodb list-tables --endpoint-url http://localhost:8000 | grep master
# 表示されるはず: master-command, master-data, master-history
import_tmpストリームが見つからずServerless Offlineが失敗する
症状: LOCAL_DDB_IMPORT_TMP_STREAM環境変数が設定されていないため、npm run offline:slsが失敗する。
原因: prisma/dynamodbs/にimport_tmp.jsonテーブル定義ファイルがありません。これがないと、npm run migrateがimport_tmp DynamoDBテーブルを作成できず、ストリームARNが.envに書き込まれません。
v1.1.1より前のバージョンでは、CLIでスキャフォールドされたプロジェクトにimport_tmp.jsonテンプレートが含まれていませんでした。これはバージョン1.1.1で修正されました。
解決策:
以下の内容でprisma/dynamodbs/import_tmp.jsonを作成してください:
{
"TableName": "import_tmp",
"AttributeDefinitions": [
{ "AttributeName": "pk", "AttributeType": "S" },
{ "AttributeName": "sk", "AttributeType": "S" }
],
"KeySchema": [
{ "AttributeName": "pk", "KeyType": "HASH" },
{ "AttributeName": "sk", "KeyType": "RANGE" }
],
"BillingMode": "PAY_PER_REQUEST",
"StreamSpecification": {
"StreamEnabled": true,
"StreamViewType": "NEW_IMAGE"
},
"TableClass": "STANDARD",
"DeletionProtectionEnabled": true
}
その後、マイグレーションを再実行してください:
npm run migrate
マイグレーションスクリプトが自動的にテーブルを作成し、LOCAL_DDB_IMPORT_TMP_STREAMエントリを.envファイルに追加します。
バージョン競合 (HTTP 409)
症状: アイテムの更新時にHTTP 409 Conflictが返される。
原因: 2つの同時リクエストが同じバージョンで同じアイテムを更新しようとしました。フレームワークはDynamoDBの条件付き書き込み(楽観的ロッ ク)を使用しており、2つの更新が競合した場合、2番目の更新はConditionalCheckFailedExceptionで失敗し、フレームワークがHTTP 409に変換します。
解決策:
- 最新バージョンを取得した後、更新を再試行する:
import { ConditionalCheckFailedException } from '@aws-sdk/client-dynamodb';
import { CommandService, DataService, IInvoke } from '@mbc-cqrs-serverless/core';
async function updateWithRetry(
commandService: CommandService,
dataService: DataService,
pk: string,
sk: string,
updateData: object,
invokeContext: IInvoke,
maxRetries = 3,
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const current = await dataService.getItem({ pk, sk });
return await commandService.publishAsync(
{ pk, sk, ...updateData, version: current?.version },
{ invokeContext },
);
} catch (error) {
if (
error instanceof ConditionalCheckFailedException ||
(error as Error).name === 'ConditionalCheckFailedException'
) {
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded due to version conflicts');
}
- または
VERSION_LATEST = -1を使用してバージョンチェックをスキップする(最後の書き込みが優先):
import { VERSION_LATEST } from '@mbc-cqrs-serverless/core';
await commandService.publishAsync(
{ pk, sk, ...updateData, version: VERSION_LATEST },
{ invokeContext },
);
参照: バージョン競合ガイド
DynamoDBスループット超過
症状: ProvisionedThroughputExceededExceptionエラー。
解決策:
- 開発用:オンデマンド課金モードを使用
- 本番用:プロビジョニング容量を増やすか、オートスケーリングを有効化
// CDK configuration for on-demand (オンデマンド用CDK設定)
const table = new dynamodb.Table(this, 'Table', {
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
Lambdaエラー
Lambdaタイムアウト
症状: タスクがX秒後にタイムアウトした。
解決策:
- CDKでタイムアウトを延長:
const handler = new lambda.Function(this, 'Handler', {
timeout: cdk.Duration.seconds(30),
});
- コールドスタートを最適化:
- バンドルサイズを縮小
- 重要な関数にはプロビジョニング済み同時実行を使用
- 初期化処理をハンドラー外に移動
Lambdaメモリ不足
症状: Runtime.ExitErrorまたはメモリ制限超過。
解決策:
const handler = new lambda.Function(this, 'Handler', {
memorySize: 1024, // メモリを増やす
});
Lambdaでモジュールが見つからない
症状: モジュール 'xxx' が見つからないエラー。
解決策:
- バンドリング設定を確認:
// Ensure dependencies are bundled (依存関係がバンドルされていることを確認)
const handler = new lambda_nodejs.NodejsFunction(this, 'Handler', {
bundling: {
externalModules: [], // 何も除外しない
},
});
- package.jsonの依存関係が正しいことを確認
認証エラー
Cognitoトークンが無効
症状: 401 Unauthorizedまたはトークン検証の失敗。
解決策:
- Cognito設定を確認:
# Check USER_POOL_ID and USER_POOL_CLIENT_ID match (USER_POOL_IDとUSER_POOL_CLIENT_IDが一致することを確認)
COGNITO_USER_POOL_ID=ap-northeast-1_xxxxxx
COGNITO_USER_POOL_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxx
- トークンの有効期限を確認:
- アクセストークンはデフォルトで1時間後に期限切れ
- トークンリフレッシュロジックを実装
- 発行者URLを確認:
const issuer = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`;
GroupRoleResolver が起動時に DI スコープ競合を引き起こす (AP027)
症状: アプリケーションが起動に失敗し、GroupRoleResolver に関するスコープ競合または重複プロバイダーの NestJS 依存性注入エラーが発生する。
原因: リゾルバークラスに @GroupRoleResolver() と @Injectable() の両方のデコレーターが付いている。@GroupRoleResolver() はすでにクラスをシングルトンプロバイダーとして登録しており、@Injectable() を追加すると競合する二重登録が発生する(AP027 アンチパターン)。
解決策: リゾルバークラスから @Injectable() を削除する:
// 誤り — AP027 が発生: 両デコレーターが競合
@Injectable()
@GroupRoleResolver()
export class AppGroupRoleResolver implements IGroupRoleResolver { ... }
// 正しい — @GroupRoleResolver() のみ
@GroupRoleResolver()
export class AppGroupRoleResolver implements IGroupRoleResolver { ... }
mbc_check_anti_patterns MCP ツールはこれを AP027 として検出します。完全な使い方は認証 — グループベース ロールを参照してください。
CORSエラー
症状: ブラウザでAccess-Control-Allow-Originエラー。
解決策:
- API GatewayでCORSを設定:
const api = new apigateway.HttpApi(this, 'Api', {
corsPreflight: {
allowOrigins: ['http://localhost:3000', 'https://your-domain.com'],
allowMethods: [apigateway.CorsHttpMethod.ANY],
allowHeaders: ['Authorization', 'Content-Type'],
},
});
- OPTIONSリクエストが処理されていることを確認
イベント処理
イベントが処理されない
症状: DynamoDB StreamsまたはSQSメッセージがハンドラーをトリガーしない。
解決策:
- イベントソースマッピングを確認:
aws lambda list-event-source-mappings --function-name your-function
- ハンドラーが登録されていることを確認:
@EventHandler(YourEvent)
export class YourEventHandler implements IEventHandler<YourEvent> {
async execute(event: YourEvent): Promise<void> {
// Handler implementation (ハンドラーの実装)
}
}
- CloudWatch Logsでエラーを確認
重複イベント処理
症状: 同じイベントが複数回処理される。
解決策:
- 冪等性を実装:
// Use a unique identifier to check if already processed (一意の識別子を使用して処理済みかを確認)
// For commands, use pk + sk + version as the idempotency key (コマンドの場合、pk + sk + versionを冪等性キーとして使用)
const idempotencyKey = `${command.pk}#${command.sk}@${command.version}`;
if (await this.isProcessed(idempotencyKey)) {
return; // Skip duplicate (重複をスキップ)
}
- SQS可視性タイムアウトを適切に設定
Step Functions
Step Functions実行の失敗
症状: ステートマシンの実行がエラーで失敗する。
解決策:
-
AWSコンソールで実行履歴を確認:
- Step Functions → ステートマシン → 対象のマシン に移動
- 失敗した実行をクリック
- 各ステップでエラーの詳細を確認
-
エラーハンドリングを追加:
// Add retry and catch in state machine definition (ステートマシン定義にリトライとキャッチを追加)
{
"Retry": [
{
"ErrorEquals": ["States.TaskFailed"],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": ["States.ALL"],
"Next": "HandleError"
}
]
}
Step Functionsタイムアウト
症状: 実行がタイムアウトする。
解決策:
- ステートマシン定義でタイムアウトを延長
- 長時間実行タスクを小さなステップに分割
- 非同期操作にはコールバック付きのWait状態を使用
デプロイの問題
CDKデプロイの失敗
症状: cdk deployがCloudFormationエ ラーで失敗する。
解決策:
- CloudFormationイベントを確認:
aws cloudformation describe-stack-events --stack-name YourStack
-
よくある原因:
- IAM権限の問題
- リソース制限超過
- 無効なリソース設定
-
ロールバックして修正:
aws cloudformation delete-stack --stack-name YourStack
# Fix the issue and redeploy (問題を修正して再デプロイ)
cdk deploy
リソースが既に存在する
症状: 名前Xのリソースが既に存在する。
解決策:
- ユニークな命名を使用:
const bucket = new s3.Bucket(this, 'Bucket', {
bucketName: `${props.appName}-${props.envName}-${cdk.Aws.ACCOUNT_ID}`,
});
- またはbucketNameを指定せずにCDKに名前を生成させる
パフォーマンスの問題
APIレスポンスが遅い
症状: APIレスポンスに時間がかかりすぎる。
解決策:
- Lambdaプロビジョニング済み同時実行を有効化
- API GatewayまたはDAXでキャッシュを実装
- データベースクエリを最適化
- RDSにコネクションプーリングを使用
Lambda高コスト
症状: 予想外に高いLambda課金。
解決策:
- 呼び出し回数と実行時間を確認
- メモリ割り当てを最適化(メモリが多い=実行が速い)
- リクエストバッチングを実装
- 予約済み同時実行でスケーリングを制限
通知とリアルタイムイベント
AppSync Events API が通知を配信しない
症状: コマンド処理後、AppSync Events API 経由でサブスクライブしたクライアントが通知を受信しない (v1.3.0+)。
チェックリスト:
- 環境変数が設定されていることを確認する:
NOTIFICATION_TRANSPORTS=appsync-event
# または、GraphQL と Events API の両方にデュアルパブリッシュする場合:
NOTIFICATION_TRANSPORTS=appsync-graphql,appsync-event
APPSYNC_EVENTS_ENDPOINTが Events API の URL を指していることを確認する —/graphqlではなく/eventで終わる:
# 正しい例
APPSYNC_EVENTS_ENDPOINT=https://xxxx.appsync-api.ap-northeast-1.amazonaws.com/event
-
IAM 権限を確認する — Lambda/ECS 実行ロールに、Event API ARN に対する
appsync:EventPublishが必要。CDK テンプレートはgrantPublish()で自動的に追加する。 -
チャネルの名前空間を確認する —
APPSYNC_EVENTS_NAMESPACEは AppSync Event API に事前作成された名前空間と一致する必要がある(デフォルト:default)。 -
CloudWatch ログでパブリッシュエラーを確認する:
aws logs filter-log-events \
--log-group-name /aws/lambda/your-function \
--filter-pattern "appsync"
完全なセットアップ手順は AppSync Events API を参照してください。
ヘルプを得る
ここで解決策が見つからない場合:
- デバッグガイドで調査テクニックを確認
- GitHubで既存のIssueを検索
- 以下を含む新しいIssueを作成:
- 問題の明確な説明
- 再現手順
- エラ ーメッセージとログ
- 環境の詳細(Nodeバージョン、OSなど)
関連ドキュメント
- デバッグガイド - 詳細なデバッグ手順
- エラーカタログ - エラーコードと解決策
- バージョン競合ガイド - HTTP 409バージョン競合の対処法
- 監視とログ - 本番監視の設定