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にリクエストを送ると以下のように正しくリクエストが返って来ました。