メインコンテンツまでスキップ

よくある問題

このページでは、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に接続できない。

解決策:

  1. DynamoDB Localが実行中であることを確認:
docker ps | grep dynamodb
  1. 設定でエンドポイントURLを確認:
// Should be http://localhost:8000 for local development (ローカル開発では http://localhost:8000 を使用)
dynamodbEndpoint: 'http://localhost:8000'
  1. テーブルが存在することを確認:
aws dynamodb list-tables --endpoint-url http://localhost:8000

Prismaマイグレーションエラー

症状: Prisma migrateが接続エラーで失敗する。

解決策:

  1. データベースコンテナが起動していることを確認します(スキャフォールドされたプロジェクトはデフォルトでMySQLを使用します):
docker ps | grep mysql
  1. .envのDATABASE_URLがデータベースと一致していることを確認します。スキャフォールドされたプロジェクトのデフォルトはMySQLです:
DATABASE_URL="mysql://root:RootCqrs@localhost:3306/cqrs?schema=public&connection_limit=1"
  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エラーで失敗します。

解決策:

  1. 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"
  1. テーブルがない場合は、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")
}
  1. Prismaマイグレーションを実行:
npx prisma migrate dev --name add_master_table
  1. 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 migrateimport_tmp DynamoDBテーブルを作成できず、ストリームARNが.envに書き込まれません。

既知の問題(v1.1.1で修正済み)

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に変換します。

解決策:

  1. 最新バージョンを取得した後、更新を再試行する:
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');
}
  1. または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秒後にタイムアウトした。

解決策:

  1. CDKでタイムアウトを延長:
const handler = new lambda.Function(this, 'Handler', {
timeout: cdk.Duration.seconds(30),
});
  1. コールドスタートを最適化:
  • バンドルサイズを縮小
  • 重要な関数にはプロビジョニング済み同時実行を使用
  • 初期化処理をハンドラー外に移動

Lambdaメモリ不足

症状: Runtime.ExitErrorまたはメモリ制限超過。

解決策:

const handler = new lambda.Function(this, 'Handler', {
memorySize: 1024, // メモリを増やす
});

Lambdaでモジュールが見つからない

症状: モジュール 'xxx' が見つからないエラー。

解決策:

  1. バンドリング設定を確認:
// Ensure dependencies are bundled (依存関係がバンドルされていることを確認)
const handler = new lambda_nodejs.NodejsFunction(this, 'Handler', {
bundling: {
externalModules: [], // 何も除外しない
},
});
  1. package.jsonの依存関係が正しいことを確認

認証エラー

Cognitoトークンが無効

症状: 401 Unauthorizedまたはトークン検証の失敗。

解決策:

  1. 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. トークンの有効期限を確認:
  • アクセストークンはデフォルトで1時間後に期限切れ
  • トークンリフレッシュロジックを実装
  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エラー。

解決策:

  1. 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'],
},
});
  1. OPTIONSリクエストが処理されていることを確認

イベント処理

イベントが処理されない

症状: DynamoDB StreamsまたはSQSメッセージがハンドラーをトリガーしない。

解決策:

  1. イベントソースマッピングを確認:
aws lambda list-event-source-mappings --function-name your-function
  1. ハンドラーが登録されていることを確認:
@EventHandler(YourEvent)
export class YourEventHandler implements IEventHandler<YourEvent> {
async execute(event: YourEvent): Promise<void> {
// Handler implementation (ハンドラーの実装)
}
}
  1. CloudWatch Logsでエラーを確認

重複イベント処理

症状: 同じイベントが複数回処理される。

解決策:

  1. 冪等性を実装:
// 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 (重複をスキップ)
}
  1. SQS可視性タイムアウトを適切に設定

Step Functions

Step Functions実行の失敗

症状: ステートマシンの実行がエラーで失敗する。

解決策:

  1. AWSコンソールで実行履歴を確認:

    • Step Functions → ステートマシン → 対象のマシン に移動
    • 失敗した実行をクリック
    • 各ステップでエラーの詳細を確認
  2. エラーハンドリングを追加:

// 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エラーで失敗する。

解決策:

  1. CloudFormationイベントを確認:
aws cloudformation describe-stack-events --stack-name YourStack
  1. よくある原因:

    • IAM権限の問題
    • リソース制限超過
    • 無効なリソース設定
  2. ロールバックして修正:

aws cloudformation delete-stack --stack-name YourStack
# Fix the issue and redeploy (問題を修正して再デプロイ)
cdk deploy

リソースが既に存在する

症状: 名前Xのリソースが既に存在する。

解決策:

  1. ユニークな命名を使用:
const bucket = new s3.Bucket(this, 'Bucket', {
bucketName: `${props.appName}-${props.envName}-${cdk.Aws.ACCOUNT_ID}`,
});
  1. またはbucketNameを指定せずにCDKに名前を生成させる

パフォーマンスの問題

APIレスポンスが遅い

症状: APIレスポンスに時間がかかりすぎる。

解決策:

  1. Lambdaプロビジョニング済み同時実行を有効化
  2. API GatewayまたはDAXでキャッシュを実装
  3. データベースクエリを最適化
  4. RDSにコネクションプーリングを使用

Lambda高コスト

症状: 予想外に高いLambda課金。

解決策:

  1. 呼び出し回数と実行時間を確認
  2. メモリ割り当てを最適化(メモリが多い=実行が速い)
  3. リクエストバッチングを実装
  4. 予約済み同時実行でスケーリングを制限

通知とリアルタイムイベント

AppSync Events API が通知を配信しない

症状: コマンド処理後、AppSync Events API 経由でサブスクライブしたクライアントが通知を受信しない (v1.3.0+)。

チェックリスト:

  1. 環境変数が設定されていることを確認する:
NOTIFICATION_TRANSPORTS=appsync-event
# または、GraphQL と Events API の両方にデュアルパブリッシュする場合:
NOTIFICATION_TRANSPORTS=appsync-graphql,appsync-event
  1. APPSYNC_EVENTS_ENDPOINT が Events API の URL を指していることを確認する — /graphql ではなく /event で終わる:
# 正しい例
APPSYNC_EVENTS_ENDPOINT=https://xxxx.appsync-api.ap-northeast-1.amazonaws.com/event
  1. IAM 権限を確認する — Lambda/ECS 実行ロールに、Event API ARN に対する appsync:EventPublish が必要。CDK テンプレートは grantPublish() で自動的に追加する。

  2. チャネルの名前空間を確認する — APPSYNC_EVENTS_NAMESPACE は AppSync Event API に事前作成された名前空間と一致する必要がある(デフォルト: default)。

  3. CloudWatch ログでパブリッシュエラーを確認する:

aws logs filter-log-events \
--log-group-name /aws/lambda/your-function \
--filter-pattern "appsync"

完全なセットアップ手順は AppSync Events API を参照してください。

ヘルプを得る

ここで解決策が見つからない場合:

  1. デバッグガイドで調査テクニックを確認
  2. GitHubで既存のIssueを検索
  3. 以下を含む新しいIssueを作成:
    • 問題の明確な説明
    • 再現手順
    • エラーメッセージとログ
    • 環境の詳細(Nodeバージョン、OSなど)

関連ドキュメント