この記事について
AWS CDKv2でCRUD操作ができるAPIを作る
今回はTypeScriptを使う
アプリの構成図
API Gateway + Lambda + DynamoDBでTODOオブジェクトのCRUD操作ができるAPIを作る
プロジェクト初期化
CLIのインストール
まずはCDK CLIをインストールする
npm install -g aws-cdk
cdk --version
バージョンが表示されればOK
CDK Toolkitのデプロイ
初回のみCDKを利用する際に必要となるファイル群のデプロイが必要
cdk bootstrap aws://${ACCOUNT}/${REGION}
以下のように表示されればOK
✅ Environment aws://${ACCOUNT}/${REGION} bootstrapped.
※異なるアカウント、リージョンでCDKを利用する場合はまたこの作業が必要になる
プロジェクト初期化
適当なディレクトリで↓を実行
cdk init app --language typescript
実装
CDKのコードを書く
スタックの実装
/lib以下のファイルにスタック(CDKでデプロイするリソース群の単位)を実装する
import { RemovalPolicy, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda-nodejs";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as logs from "aws-cdk-lib/aws-logs";
import * as apigateway from "aws-cdk-lib/aws-apigateway";
export class TodoappTsStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// DynamoDB
const todosTable = new dynamodb.Table(this, "TodosTable", {
partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
tableName: "todoapp-ts-todos-table",
removalPolicy: RemovalPolicy.DESTROY,
});
// Lambda
const todoFunction = new lambda.NodejsFunction(this, "TodoFunction", {
entry: "src/main.ts", // Lambdaのソースコードのエントリポイント
handler: "handler", // "entry"で指定したファイルのexportしている関数名
environment: {
TODOS_TABLE_NAME: todosTable.tableName,
},
functionName: "todoapp-ts-function",
});
// LambdaにDynamoDBのCRUD操作権限を付与
todoFunction.addToRolePolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"dynamodb:Scan",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
],
resources: [todosTable.tableArn],
})
);
// LogGroup
new logs.LogGroup(this, "TodoFunctionLogs", {
logGroupName: "/aws/lambda/" + todoFunction.functionName,
removalPolicy: RemovalPolicy.DESTROY,
});
// API Gateway
const api = new apigateway.RestApi(this, "TodoApi", {
restApiName: "todoapp-ts-todo-api",
defaultCorsPreflightOptions: {
allowOrigins: apigateway.Cors.ALL_ORIGINS,
allowMethods: apigateway.Cors.ALL_METHODS,
allowHeaders: apigateway.Cors.DEFAULT_HEADERS,
statusCode: 200,
},
});
const integration = new apigateway.LambdaIntegration(todoFunction);
// /todos
const todosResource = api.root.addResource("todos");
todosResource.addMethod("GET", integration);
todosResource.addMethod("POST", integration);
// /todos/{id}
const todoIdResource = todosResource.addResource("{id}");
todoIdResource.addMethod("PUT", integration);
todoIdResource.addMethod("DELETE", integration);
}
}
Lambdaについて
Lambdaの作成にはaws-lambda-nodejsを使っている
このモジュールを使うとTypeScriptのコードをjavascriptに変換する作業(トランスパイル)をCDKのデプロイ時によしなにやってくれる
ちなみにPythonとGoにも同様のモジュールが用意されているもののまだアルファ版
javaと.NETにはないっぽい
Lambdaのコード実装
こちらのサンプルコードを参照
今回はexpressでルーティングを、serverless-expressでAPI Gatewayのイベントとの結合を実装してみた
デプロイ
↓を実行
cdk deploy TodoappTsStack
ちなみに、スタック名などは/bin/todoapp-ts.tsで指定している
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { TodoappTsStack } from "../lib/todoapp-ts-stack";
const app = new cdk.App();
new TodoappTsStack(app, "TodoappTsStack"); // ここの"TodoappTsStack"を指定する
/libにスタックを追加した場合はこのファイル内でインスタンス生成してあげればOK
動作確認
デプロイが完了したら以下のスクリプトを実行
#!/bin/bash
ENDPOINT="API Gatewayのエンドポイント"
RESPONSE=$(curl "${ENDPOINT}/todos" \
-X POST \
-H "Content-Type: application/json" \
-d '{"message":"post todo"}' \
-w"\n")
echo ${RESPONSE}
TODO_ID=$(echo ${RESPONSE} | jq .data.todo.id -r)
curl "${ENDPOINT}/todos" \
-w"\n"
curl "${ENDPOINT}/todos/${TODO_ID}" \
-X PUT \
-H "Content-Type: application/json" \
-d '{"message":"put todo"}' \
-w"\n"
curl "${ENDPOINT}/todos/${TODO_ID}" \
-X DELETE \
-w"\n"
こんな感じで出力されていれば成功
{"message":"Post todo succeed.","data":{"todo":{"id":"39593ece-2bc7-4456-b52f-3265494b8331","message":"post todo"}}}
{"message":"Get todos succeed.","data":{"todos":[{"id":"39593ece-2bc7-4456-b52f-3265494b8331","message":"post todo"}]}}
{"message":"Put todo succeed.","data":{"todo":{"id":"39593ece-2bc7-4456-b52f-3265494b8331","message":"put todo"}}}
{"message":"Delete todo succeed."}
感想
TypeScriptの静的型付けによってCloudFormationをプログラミング言語で書けるCDKの恩恵を最大限に受けられた
また、Webアプリの場合はクライアントもTypeScriptで書くことが多いので言語を統一できるのもGood
LambdaのコードをTypeScriptで書いた場合のトランスパイルについてもそれ用のモジュールがあるので全く問題ない
CDKのサンプルコードや記事なども他の言語に比べて多いのもうれしい
総じて使い勝手は良好なのでWebアプリのプロジェクトならTypeScriptが丸いと思う
参考リンク
サンプルコード
https://github.com/tetsu0000/todoapp-ts
AWS公式ドキュメント
https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html
aws-sdk-v3の公式ドキュメント
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/index.html
serverless-express
https://github.com/vendia/serverless-express

