【AWS CDK】ApiGateway × Lambda サーバーレスAPIのテンプレートを作成しました
ApiGateway×Lambdaを使ったサーバーレスAPIのインフラをコード化するタスクを任せていただいたので、そのときに得たCDKまわりの知見を共有します。
CDKコードはテンプレートとして使っていただければと思います!
ApiGateway
ApiGatewayに関する設定を記述したクラスです。
今回は必要最小限のためモジュール化するメリットは感じられませんが、実際はログやキャッシュ等の設定を追加していきます。
import * as apigw from "aws-cdk-lib/aws-apigateway"
import * as env from "../config/env"
import { Construct } from "constructs"
export class ApiGateway {
public api: apigw.RestApi
constructor(scope: Construct) {
this.api = new apigw.RestApi(scope, `${env.APP_NAME}-apigw`, {
restApiName: `${env.APP_NAME}Api`
})
}
}
Lambda
Lambdaに関する設定を記述したクラスです。
今回はあくまでテンプレなのでLambdaのロジック部分の記載は省略します。
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
import * as lambda from "aws-cdk-lib/aws-lambda"
import { Construct } from "constructs"
import { SubnetType } from "aws-cdk-lib/aws-ec2"
import * as iam from "aws-cdk-lib/aws-iam"
import { RetentionDays } from "aws-cdk-lib/aws-logs"
export class Lambda {
private func: lambda.Function
private scope: Construct
constructor(scope: Construct) {
this.scope = scope
}
build(id: string, handler: string) {
// Lambdaに付与するIAMポリシー・ロールの設定
const vpcPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaVPCAccessExecutionRole")
const secretsManagerPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName("SecretsManagerReadWrite")
const rdsPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonRDSDataFullAccess")
const role = new iam.Role(this.scope, `iamRoleForLambda${id}`, {
roleName: `lambda-role-${id}`,
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
managedPolicies: [vpcPolicy, rdsPolicy, kmsPolicy, secretsManagerPolicy]
})
this.func = new lambda.Function(this.scope, id, {
functionName: id,
role: role,
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',// ファイル名.メソッド名
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/')), // Lambdaコードを配置してあるフォルダを指定
vpcSubnets: {
// VPC内に配置する必要あり
subnetType: SubnetType.PRIVATE_WITH_NAT
}
})
return this.func
}
}
IAMポリシー名の確認方法
fromAwsManagedPolicyNameメソッドの引数は文字列型で、ドキュメントを読んでも指定できるポリシー名ががわからないので、IAMポリシーの一覧をAWSのマネジメントコンソールから確認します。
LambdaからRDSにアクセスするには、LambdaをVPC内に配置する必要があり、VPC内に設置したLambdaからRDSにアクセスするには、Lambda用のIAMロールにAWSLambdaVPCAccessExecutionRoleを付与する必要があります。
-
IAMポリシー選択画面から検索して
-
ポリシー詳細ページでポリシーARN名を確認する
↓↓ ポリシーARN名部分 拡大表示 ↓↓
arn:aws:iam::aws:policy/ 以降の部分が今回指定する箇所になるので、こちらの文字列をfromAwsManagedPolicyNameメソッドの引数に指定します。
const vpcPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaVPCAccessExecutionRole")
ちなみに今回はservice-role/AWSLambdaVPCAccessExecutionRole
のように、ポリシー名直前が /service-roleですが、すべてのAWSのサービスがservice-role/であるというわけではないので、都度ポリシー詳細画面で確認するようにしてください。
ApiFactory
コンポーネント化したApiGatewayクラスとLambdaクラスを使って、エンドポイントを複数作成します。
export class ApiGatewayFactory {
private scope: Construct
private lambda: Lambda
public apigw: apigw.RestApi
constructor(scope: Construct, vpc: VPC, env: { rdsClusterArn: string; secretsManagerArn: string }) {
this.scope = scope
this.lambda = new Lambda(this.scope, vpc.vpc, env, [vpc.privateSubnet1a, vpc.privateSubnet1c])
this.apigw = new ApiGateway(this.scope).api
this.buildApi()
}
private buildApi() {
this.buildUsersApi()
this.buildPostsApi()
this.buildCommentsApi()
}
private buildUsersApi() {
// [GET]/users
const users = this.apigw.root.addResource("users")
const getUsersFnc = this.lambda.build(`${env.APP_NAME}GetUsers`, "getUsers")
users.addMethod("GET", new apigw.LambdaIntegration(getUsersFnc))
// [POST]/users
const postUsersFnc = this.lambda.build(`${env.APP_NAME}PostUsers`, "postUsers")
users.addMethod("POST", new apigw.LambdaIntegration(postUsersFnc))
// [PUT]/users/:id
const userId = this.apigw.root.addResource("{id}")
const putUsersFnc = this.lambda.build(`${env.APP_NAME}PutUser`, "putUser")
userId.addMethod("PUT", new apigw.LambdaIntegration(putUsersFnc))
// [DELETE]/users/:id
const deleteUsersFnc = this.lambda.build(`${env.APP_NAME}DeleteUser`, "deleteUser")
userId.addMethod("DELETE", new apigw.LambdaIntegration(deleteUsersFnc))
}
// ...
}
project-cdk-stack.ts(エントリポイント)
import { ApiGatewayFactory } from "./../utils/ApiGatewayFactory"
import { Stack, StackProps } from "aws-cdk-lib"
import { Construct } from "constructs"
class ProjectCdkStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props)
// Rdsクラス, SecretsManagersクラス インスタンス生成(省略)
// const rds = ...
// const secretsManagers = ...
new ApiGatewayFactory(this, new VPC(this), { rdsClusterArn: secretsManagers.getArn(), secretsManagerArn: rds.cluster.clusterArn })
}
}
new ProjectCdkStack(app, "ProjectCdkStack", {
env: {
region: "ap-northeast-1" //CDKで定義したリソースをデプロイするリージョンを指定
}
})
最後に
今回はApiGateway×Lambdaを用いたサーバーレスAPIを作成してみました。
コードを必要に応じて変更していただくことで、手元でサクッと実行できるAPIが作れるかと思います。
なお、今回はVPC・ RDS・SecretsManager部分を省略しましたが、また別の記事でご紹介できればと思っています。
最後までお読み頂き有難うございました!!
Discussion
丁度取り組んでいたの課題でして非常に参考になりました!
今後も更新楽しみにしております👍🏼