quartaela
quartaela

Reputation: 2747

How to add multiple base path mappings from different projects into the same AWS API Gateway?

We have separate AWS CDK projects for different APIs. We want to use same subdomain with different base path mappings for the same API Gateway resource. For example; lets say we have two APIs which are tenantApi and invoiceApi mapping to test.example.com/tenant and test.example.com/invoice. This is doable from one repository with creating one RestApi and defining multiple base path mappings to it. However, I couldn't find a way to achieve this doing it from different repositories since I need to create only one ARecord for the subdomain. My thought was creating ARecord inside a repository where we manage shared resources and importing that record from the repositories we will use the same Api Gateway.

Here is the simple aws cdk code about how I am creating an Api Gateway. As you can see, we have to pass RestApi instance into route53.RecordTarget.fromAlias so I am not really sure if we can create a ARecord before creating an Api Gateway.

export class ApiGatewayStack extends Stack {
  constructor(scope: Construct, id: string, props: StackEnvProps) {
    super(scope, id, props);

    const tenantApi = new apigateway.RestApi(this, 'tenantApi', {
      domainName: {
        domainName: props.context['domainName'],
        certificate: acm.Certificate.fromCertificateArn(this, 'certificateArn', props.context['certificateArn']),
        basePath: 'tenant'
      },
      deploy: true,
      deployOptions: {
        stageName: 'prod',
      },
      defaultCorsPreflightOptions: {
        allowMethods: apigateway.Cors.ALL_METHODS,
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
      }
    });

    const zone = route53.HostedZone.fromLookup(this, 'Zone', { domainName: 'example.com' });

    // create an alias for mapping
    new route53.ARecord(this, 'domainAliasRecord', {
      zone: zone,
      recordName: "test",
      target: route53.RecordTarget.fromAlias(new ApiGateway(tenantApi)),
    });

    const methodOptions: apigateway.MethodOptions = {
      methodResponses: [
        {
          statusCode: '200',
          responseParameters: {
            'method.response.header.Content-Type': true,
          },
        },
        {
          statusCode: '400',
          responseParameters: {
            'method.response.header.Content-Type': true,
          },
        },
      ],
    };

    const postPaymentsLambda = new NodejsFunction(this, 'postTenantLambda', {
      entry: './lambda/rest/tenant-api/post-tenant-api.ts',
      handler: 'handler',
      memorySize: 512,
      runtime: lambda.Runtime.NODEJS_14_X,
    });

    // tenant/v1
    const tenantV1 = tenantApi.root.addResource('v1');
    tenantV1.addMethod('POST', new apigateway.LambdaIntegration(postPaymentsLambda), methodOptions);

  }
}

I appreciate for any help. Thanks!

Upvotes: 2

Views: 1548

Answers (1)

quartaela
quartaela

Reputation: 2747

I had to first create a domainName then create an ARecord with targeting that domainName which can be imported from different APIs I want to attach.

// create domain name for api gateway
const domainName = new apigateway.DomainName(this, 'domainName', {
  domainName: `test.${props.domainName}`,
  certificate: acm.Certificate.fromCertificateArn(this, 'certificateArn', props.certificateArn),
  endpointType: apigateway.EndpointType.REGIONAL,
  securityPolicy: apigateway.SecurityPolicy.TLS_1_2,
});

const zone = route53.HostedZone.fromLookup(this, 'hostedZone', {
  domainName: props.context['domainName']
});

// create an alias for mapping
new route53.ARecord(this, 'apiGatewayDomainAliasRecord', {
  zone: zone,
  recordName: 'test',
  target: route53.RecordTarget.fromAlias(new r53target.ApiGatewayDomain(domainName)),
});

new CfnOutput(this, 'apiGatewayDomainNameAliasTarget', {
  value: domainName.domainNameAliasDomainName,
  description: 'domainNameAliasTarget attribute used when importing domain name',
  exportName: 'apiGatewayDomainNameAliasTarget'
});

Later on, I will import this domainName to create a BasePathMapping. There are three attributes used when importing a domainName;

  • domainName: the domainName we created before.
  • domainNameAliasHostedZoneId: Hosted ZoneId where the domain is defined.
  • domainNameAliasTarget: AWS documentation doesn't clearly state out about what it is. Basically, it is the domainNameAliasDomainName value of the domainName we created at the very first place.
const tenantApi = new apigateway.RestApi(this, 'tenantApi', {
  deployOptions: {
    stageName: 'dev',
  },
  deploy: true,
  defaultCorsPreflightOptions: {
    allowMethods: apigateway.Cors.ALL_METHODS,
    allowOrigins: apigateway.Cors.ALL_ORIGINS,
  }
});

const domainName = apigateway.DomainName.fromDomainNameAttributes(this, 'domainName', {
  domainName: `test.${props.domainName}`,
  domainNameAliasHostedZoneId: props.hostedZoneId,
  domainNameAliasTarget: Fn.importValue(props.apiGatewayDomainNameAliasTarget),
});

const nodeBasePathMapping = new apigateway.BasePathMapping(this, 'nodeBasePathMapping', {
  basePath: 'node',
  domainName,
  restApi: tenantApi,
});

Upvotes: 2

Related Questions