AWS CDKでAPI Gatewayのカスタムドメイン環境を構築してみた #AWSCDK

2019.11.13

はじめに

おはようございます。CX事業本部の佐藤です。

AWS CDKでAPI Gatewayのカスタムドメイン環境を作ってみました。

環境

項目 バージョン
macOS Mojave 10.14.5
node v10.16.0
npm 6.9.0
AWS CDK 1.15.0 (build bdbe3aa)

CDKでカスタムドメイン環境を構築する

事前準備

カスタムドメイン環境を構築するには、事前にドメインの取得や証明書の取得が必要です。今回は無料で取得できるドメインで試しました。

ドメインの取得

freenomなどのドメインレジストラで、無料のドメインを取得しDNSの設定をします。弊社の以下のブログが参考になります。

https://dev.classmethod.jp/cloud/aws/mesoko-r53-cdn/

Certificate ManagerでSSL証明書を取得

弊社の以下のブログが参考になります。

https://dev.classmethod.jp/cloud/aws/certificate-manager-dns-validation-support/

パラメータストアにドメイン名と取得した証明書のARNを設定します。

後ほど、CDKから参照して使用するため、設定しておきます。

aws ssm put-parameter --type 'String' --name 'APIG_CUSTOM_DOMAIN_NAME' --value 'www.example.com'
aws ssm put-parameter --type 'String' --name 'APIG_CERTIFICATE_ARN' --value 'arn:aws:XXXX'

CDKでAPI Gatewayを作成する

AWS CDKを使ってインフラを構築していきます。以下、サンプルコードです。

テスト用Lambdaコード

src/lambda/handler/api-gw/test.ts

export async function handler(event: any): Promise<any> {
  return 'test api gateway!';
}

CDKのコード

lib/apig-sample-stack.ts

import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apig from '@aws-cdk/aws-apigateway';
import { NODE_LAMBDA_LAYER_DIR } from './process/setup';
import { Certificate } from '@aws-cdk/aws-certificatemanager';
import * as ssm from '@aws-cdk/aws-ssm';

export class ApigSampleStack extends cdk.Stack {

  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // パラメータストアからドメイン名と証明書のARNを取得
    const ApigCustomDomainName = ssm.StringParameter.fromStringParameterName(this, 'CustomDomainName', 'APIG_CUSTOM_DOMAIN_NAME');
    const ApigCertificateArn = ssm.StringParameter.fromStringParameterName(this, 'CertificateArn', 'APIG_CERTIFICATE_ARN');
		
    // Lambda Functionの作成
    const testFunction = new lambda.Function(this, 'TestFunction', {
      code: lambda.Code.fromAsset('src/lambda/handlers/api-gw'),
      functionName: 'testFunction',
      handler: 'test.handler',
      runtime: lambda.Runtime.NODEJS_10_X,
      memorySize: 256,
    });

    // API Gatewayの作成
    const api = new apig.RestApi(this, 'RestApi', {
      restApiName: 'test',
      // エンドポイントタイプの設定(SSL証明書を東京リージョンで作成したため、REGIONALで作成します。)
      endpointTypes: [
        apig.EndpointType.REGIONAL
      ],
      // CORSの設定
      defaultCorsPreflightOptions: {
        allowOrigins: apig.Cors.ALL_ORIGINS,
        allowMethods: ['POST', 'OPTIONS'],
        statusCode: 200,
      },
      description: "テスト用"
    });

    // カスタムドメインの設定
    const domainName = new apig.DomainName(this, 'CustomDomain', {
      // fromCertificateArnで証明書を取得します
      certificate: Certificate.fromCertificateArn(this, 'Certificate', ApigCertificateArn.stringValue),
      // パラメータストアに登録したドメイン名を設定します
      domainName: ApigCustomDomainName.stringValue,
      // REGINALタイプでカスタムドメインを設定します
      endpointType: apig.EndpointType.REGIONAL
    });
    // ベースパスマッピングの設定
    // https://www.example.com/v1/がベースパスとなります。
    domainName.addBasePathMapping(api, {
      basePath: 'v1'
    });

    // testリソース作成
    const testResource = api.root.addResource('test');
    
    // POSTメソッドの作成
    testResource.addMethod('POST', new apig.LambdaIntegration(testFunction, {
      connectionType: apig.ConnectionType.INTERNET,
      // 統合リクエストの設定
      requestTemplates: {
        'application/json': "$input.json('$')"
      },
      // 統合レスポンスの設定(CORS)
      integrationResponses: [
        {
          statusCode: '200',
          contentHandling: apig.ContentHandling.CONVERT_TO_TEXT,
          responseParameters: {
            'method.response.header.Access-Control-Allow-Headers': "'Origin,Content-Type,Authorization'",
            'method.response.header.Access-Control-Allow-Methods': "'POST,OPTIONS'",
            'method.response.header.Access-Control-Allow-Origin': "'*'"
          },
          responseTemplates: {
            'application/json': "$input.json('$')"
          }
        }
      ],
      passthroughBehavior: apig.PassthroughBehavior.WHEN_NO_MATCH,
      proxy: false,
    }),
      {
      	// メソッドレスポンスの設定(CORS)
        methodResponses: [
          {
            statusCode: '200',
            responseParameters: {
              'method.response.header.Access-Control-Allow-Headers': true,
              'method.response.header.Access-Control-Allow-Methods': true,
              'method.response.header.Access-Control-Allow-Origin': true
            }
          }
        ]
      }
    );
  }
}

デプロイ

AWSにデプロイします。

npm run build
cdk deploy

動作確認

デプロイできたら、動作確認をしてみます。PostmanでAPIを呼び出してみます。Lambdaが実行されて、 レスポンスが返ってきています。

まとめ

AWS CDKでカスタムドメイン環境を構築してみました。当初は、CDK + Swaggerの構成を考えていたんですが、CDKのAPI GatewayライブラリがまだSwaggerのインポートに対応していないため、SwaggerはAPI仕様書として別管理とし、現状はCDK単体で構築するのが良いかと思います。誰かの参考になれば幸いです。