OpenAPIを使ってAPIの定義を行い、API GatewayのOpen API定義のインポート機能を使って行うのは、AWSでREST APIを開発する際のポピュラーなやり方ではないでしょうか。本記事ではAWS CDK(TypeScript)を使ってAPIGateway(HTTP API)とLambdaで構成されたAPIをOpenAPIの定義ファイルを使ってインポートしデプロイする方法です。
実際のソースコートは以下に上がっていますのでこちらもご参考に。
https://github.com/serverless-operations/cdk-httpapi-lambda-with-openapi
OpenAPIの定義
以下のようなhelloと返すだけのサンプルのAPIを定義します。
---
openapi: 3.0.1
info:
  title: Hello API
  description: An api always returns HELLO
  version: 1.0.0
paths:
  /hello:
    get:
      summary: Returning Hello.
      responses:
        200:
          $ref: '#/components/responses/GetHelloResponse'
components:
  schemas:
    Hello:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: message
  responses:
    GetHelloResponse:
      description: Get hello response.
      content:
         application/json:
          schema:
            $ref: '#/components/schemas/Hello'CDKの実装
APIのCloudFormationを定義していきます。以下がCDKが直接実行する最上位のデプロイスクリプトです。SwaggerParserのライブラリを使用して上記のAPI定義をパースし、APIのスタックに渡してAPI Gatewayにデプロイを行います。
#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from '@aws-cdk/core'
import { ApiStack } from './api-stack'
import * as dotenv from 'dotenv'
dotenv.config()
const SwaggerParser = require('@apidevtools/swagger-parser')
const {
  STAGE = 'dev'
} = process.env
async function createApp(): Promise<cdk.App> {
  const openApi: any = await SwaggerParser.dereference('./deploy/api-definition.yaml')
  const app = new cdk.App()
  
  new ApiStack(app, `MyApiStack-${STAGE}`, {
    stage: STAGE,
    openApi: openApi,
  })
  return app
}
createApp()以下がAPIのスタックを実装するソースコードです。HttpApiのコンストラクタはまだ記事を執筆している2021/3/13時点では、experimental のステータスのため、CfnApiコンストラクタを使用しています。ここにOpenAPIの定義を渡します。
また、APIGatewayのOpenAPI独自拡張であるx-amazon-apigateway-integrationのプロパティもコードの中で追記しています。
import * as cdk from '@aws-cdk/core'
import { Code, Function, Runtime } from '@aws-cdk/aws-lambda'
import { CfnApi, CfnStage } from '@aws-cdk/aws-apigatewayv2'
import { ServicePrincipal } from '@aws-cdk/aws-iam'
export interface ApiProps extends cdk.StackProps {
  stage: string
  openApi: any
}
interface IntegrationSetting {
  readonly type: string
  readonly httpMethod: string
  readonly uri: string
  readonly payloadFormatVersion: string
}
export class ApiStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props: ApiProps) {
    super(scope, id, props)
    const myFunction = new Function(this, 'myFunction', {
      code: Code.fromAsset('dist/handler'),
      handler: 'index.handler',
      runtime: Runtime.NODEJS_14_X
    })
    const integrationSetting: IntegrationSetting = {
      type: 'AWS_PROXY',
      httpMethod: 'POST',
      uri: myFunction.functionArn,
      payloadFormatVersion: '2.0'
    }
    
    // APIGatewayのOpenAPI独自拡張であるx-amazon-apigateway-integrationをここで追記
    Object.entries(props.openApi.paths).forEach(([ path ]) => {
      Object.entries(props.openApi.paths[path]).forEach(([ method ]) => {
        props.openApi.paths[path][method]['x-amazon-apigateway-integration'] = integrationSetting
      })
    })
    // HttpApiのコンストラクタはまだ記事を執筆している2021/3/13時点では、experimental のステータスのため、CfnApiコンストラクタを使用しています
    const api = new CfnApi(this, 'httpApi', {
      body: props.openApi
    })
    new CfnStage(this, `api-${props.stage}`, {
      apiId: api.ref,
      stageName: '$default',
      autoDeploy: true,
    })
    myFunction.addPermission(
      'myFunctionPermission',
      {
        principal: new ServicePrincipal('apigateway.amazonaws.com'),
        action: 'lambda:InvokeFunction',
        sourceArn: `arn:aws:execute-api:${cdk.Stack.of(this).region}:${cdk.Stack.of(this).account}:${api.ref}/*/*/*`
      }
    )
    new cdk.CfnOutput(this, 'HTTP API Url', {
      value: api.attrApiEndpoint ?? 'Something went wrong with the deploy'
    })
  }
}デプロイ
cdk deployコマンドでデプロイを行います。
$ cdk deploy --all以下のようにデプロイ成功しました。

OpenAPIで定義したURLにリクエストを送ると以下のように正しくリクエストが返って来ました。


