🎃

「A sibling ({}) of this resource already has a variable path part」の対応

2023/03/12に公開

前提

cdkでデプロイしたAPI Gatewayのリソースパスのパス変数名を変更しようとした際に下記のエラーが生じました。

エラーメッセージ
21:12:57 | CREATE_FAILED        | AWS::ApiGateway::Resource   | CreateApi/Default/{piyo}
Resource handler returned message: "A sibling ({hoge}) of this resource already has a variable path part -- only one is allowed (Service: ApiGa
teway, Status Code: 400, Request ID: 20d70bb5-6017-4daa-bd5b-c68c17f11c9d)" (RequestToken: 443e9cff-e29b-c640-7b03-d9657f86eac4, HandlerErrorCo
de: InvalidRequest)
スタックの変更箇所
import * as CDK from 'aws-cdk-lib';
import * as APIGATEWAY from 'aws-cdk-lib/aws-apigateway';
import { Construct } from 'constructs';

export class SetupApiStack extends CDK.Stack {
  constructor(scope: Construct, id: string, props: CDK.StackProps) {
    super(scope, id, props);

    // create mock integration
    const mockIntegration = new APIGATEWAY.MockIntegration({
      requestTemplates: {
        'application/json': '{ "statusCode": 200 }',
      },
      integrationResponses: [
        {
          statusCode: '200',
          responseTemplates: {
            'application/json': '{ "message": "ok" }',
          },
        },
      ],
    });

    // create api gateway
    const api = new APIGATEWAY.RestApi(this, 'SampleApi');

    api.root
-     .addResource('{old_path_var}')
+     .addResource('{new_path_var}')
      .addMethod('GET', mockIntegration, {
        methodResponses: [
          {
            statusCode: '200',
          },
        ],
      });
  }
}

※コードの見やすさのため、バックエンド部分はモック統合で記述しています。

原因

CloudFormationではまず新しいリソースパスを追加しようとし、次に古いリソースパスを削除しようとするようです。そのため、両方のリソースパス(/{old_path_var}/{new_path_var})が存在する中間状態が生じます。しかし、両リソースパスは変数名が異なるだけで、実体は同じものですからリソースパスの重複によりエラーとなります。

解決策

スタックから変更したいリソースパスを削除して、変更したいエンドポイントを完全に削除します。このとき、少なくとも一つメソッドを定義しておかないとエラーが起きるので、適当にパスリソースにメソッドを定義しておきます。

api.root
- .addResource('{old_path_var}')
+ .addResource('tmp_path')
  .addMethod('GET', mockIntegration, {
    methodResponses: [
      {
        statusCode: '200',
      },
    ],
  });

あとは新たなパス変数名のエンドポイントをデプロイして完了です。

api.root
- .addResource('tmp_path')
+ .addResource('{new_path_var}')
  .addMethod('GET', mockIntegration, {
    methodResponses: [
      {
        statusCode: '200',
      },
    ],
  });

そもそも一度APIリソースごと削除して作り直してもよかったのですが、今回はカスタムドメインを使用していなかったこともあり、APIごと削除してしまうとエンドポイントのホスト部が変わってしまうため、APIリソース自体は削除しないように変更しました。

参考

GitHubで編集を提案

Discussion