AWS LambdaとDynamoDBを使って、データの読み書きが行えるAPIをデプロイしてみた
はじめに
こんにちは、クラウドソリューショングループのkitajima.eです。
この記事では、AWS LambdaとDynamoDBを使って、データの読み書きが行えるAPIをデプロイしてみました。
業務でAWSのサーバレス構成のアプリに触れる機会がしばしばあります。
しかし、断片的にしか中身を学習できておらず、いざ実際に環境構築からAPI実装まで「イチから」やれと言われても何から始めていいかわからない状況でした。
そこで、「イチから」できるようにするために必要な情報を整理しつつ、環境構築から実際にAPIを作ってデプロイするところまでをやってみます。
私が現在参画している現場で使用している言語がTypeScriptのため、ここでもTypeScriptを使用しています。
目次
Lambda / DynamoDBとは
LambdaとDynamoDBはAWSのサービスです。
まずは本稿で利用するLambdaとDynamoDBの用途を説明します。
Lambda
AWS Lambdaはサーバーレスでプログラム実行ができるサービスです。
何かのイベントをきっかけに、事前に作成したコードを実行することができるため、Webアプリケーションなどを稼働するために必要な処理を行いたいプログラムを作成するだけで、あとはLambdaが実行環境となってくれます。
Lambdaの用途の例としてはバックエンドを構築し、APIリクエストを処理することなどに使われます。
ここでは、リクエストで受け取ったデータをDB連携する機能を例にLambdaを知ってもらえればと思います。
DynamoDB
DynamoDBはNoSQLのデータベースサービスです。
キーバリュー型の形式のため、データの格納と取得に特化しています。
また、項目や属性の定義が必要ないスキーマレスのテーブル構成のため、値の厳密な設計が不要で、属性は項目ごとに異なったものにすることができる点が特徴です。
テーブルに登録したアイテムを探すためにそれぞれを一意に決定するプライマリーキーを使ってアイテムの探索を素早く行うことができます。
ここでは前述のLambdaを使ってDynamoDBへのデータの登録と取得、また、AWSマネジメントコンソール上の操作でDynamoDBへのデータの登録と取得を行ってDynamoDBに慣れてもらえればと思います。
開発環境構築
事前準備として、以下が必要になります。
- Git for Windowsのインストール
- Visual Studio Codeのインストール
- Node.jsのインストール
- AWSアカウントの作成
構築手順作業の簡単な流れとしては、以下です。
- Node.jsのバージョン確認
- Serverless Frameworkのインストール
- プロジェクトの作成
- AWSのプロファイル設定
- DynamoDBの設定
Node.jsのバージョン確認
Visual Studio Codeのターミナルを使用します。
VSCodeの上部メニューバーより “ターミナル > 新しいターミナル“ を選択
ウィンドウの下部にターミナルが表示されます。
デフォルトではpowershellが選択されていますが、ここではGit Bashを使用します。
ターミナルで以下コマンドをそれぞれ実行してください。
$ npm -v
$ node -v
これでnpmとNode.jsそれぞれのバージョンが以下のように返ってくればOKです。
$ npm -v
8.19.3
$ node -v
v18.13.0
errorの場合はNode.jsのインストールが必要です。
Node.jsは以下のページからダウンロードできます。
https://nodejs.org/ja
Serverless Frameworkのインストール
ターミナルで以下コマンドを実行してください。
$ npm install -g serverless
インストール後に以下コマンドを実行してバージョンを確認してください。
$ serverless -v
これでserverlessのバージョンが返ってくればOKです。
プロジェクトの作成
TypeScriptテンプレートを使ってプロジェクトを作成することができます。
プロジェクトフォルダを作成するディレクトリへ移動してから、以下コマンドを実行してください。
$ sls create --template aws-nodejs-typescript --path sample
プロジェクトが作成できたら、プロジェクト配下へ移動し、パッケージをインストールします。
以下のコマンドを実行してください。
$ cd sample
$ npm install
$ npm install @aws-sdk/client-dynamodb
AWSのプロファイル設定
Serverless Framework が使うIAM ユーザーの作成を行います。
AWS マネジメントコンソールにログインし、ナビゲーションバー上の検索ボックスに「IAM」と入力します。
サービスの中からIAMが見つかるので、選択してIAMダッシュボードを開きます。
ナビゲーションペインから アクセス管理 > ユーザ を選択します。
「ユーザーの作成」ボタンを押下し、以下IAMユーザー作成手順を進めてください。
IAMユーザー作成手順
- ユーザー名:sample-user を設定して次へ
- ポリシーを直接アタッチする を選択
- 許可ポリシーから、administratorAccess を選択して次へ
(必要な権限のみ残して運用してください) - 設定したユーザ―名と許可ポリシーに誤りがないことを確認して「ユーザーの作成」ボタンを押下
- 作成したユーザー sample-user を選択
- セキュリティ認証情報タブから「アクセスキー」を探し、アクセスキーを作成
- ユースケースから「ローカルコード」を選択して次へ
- 「アクセスキーを作成」ボタンを押下
- アクセスキー / シークレットアクセスキーを取得することができるため、必ずメモをしてください
(後ほど使用します)
IAMユーザの作成が完了したら、ターミナルで以下コマンドを実行してください。
$ code -r ~/.aws/credentials
完了したら~/.aws/credentials
が開かれるため、AWSの接続情報を記述します。
xxxxxx部分にIAMユーザー作成手順で取得したアクセスキー / シークレットアクセスキーをそれぞれ記述します。
[sample-user]
aws_access_key_id= xxxxxx
aws_secret_access_key= xxxxxx
テンプレートを使用して作成したsampleの中から sample/serverless.ts
を探します。
sample/serverless.ts
で対象のリージョンを設定します。
const serverlessConfiguration: AWS = {
service: "sample",
frameworkVersion: "3",
plugins: ["serverless-esbuild"],
provider: {
name: "aws",
runtime: "nodejs18.x",
apiGateway: {
minimumCompressionSize: 1024,
shouldStartNameWithService: true,
},
environment: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1",
NODE_OPTIONS: "--enable-source-maps --stack-trace-limit=1000",
},
// リージョンの設定(東京)
region: "ap-northeast-1",
},
// ...
};
ここまで出来たら一度デプロイしてみましょう。
以下のコマンドを実行してください。
$ sls deploy --aws-profile sample-user --verbose
完了したら、AWSマネジメントコンソールのナビゲーションバー上の検索ボックスに「Lambda」と入力します。
サービスの中からLambdaが見つかるので、選択してLambdaダッシュボードを開きます。
ナビゲーションペインから 関数 を選択します。
lambdaが作成されていることが確認できます。
これでLambda関数のデプロイができるようになりました。
それではテンプレートで作成された hello
という関数を以下の手順でLambdaのテスト機能を使って動かしてみます。
Lambdaテスト手順
- Lambda関数の画面にある関数名から「sample-dev-hello」を選択
- 「テスト」タブを選択し、テストイベントが開かれることを確認
- 「イベントJSON」フィールドにJSON形式でリクエスト本文を入力し、「テスト」ボタンを押下
ここでは以下のJSONを入力します。{ "headers": { "Content-Type": "application/json" }, "body": "{\"name\": \"Frederic\"}" }
- テストが成功したログが表示されることを確認してください
テンプレートで作成された hello
という関数は、リクエスト本文で渡されたJSONの name
の値がメッセージの一部に含まれて返却されるというものでした。
これでLambdaのテスト機能の使い方がわかりました!
一度サービスを削除する場合は、以下コマンドを実行してください。
$ sls remove --aws-profile sample-user --verbose
DynamoDBの設定
DynamoDBにデータを追加するためのテーブル作成を行います。
sample/serverless.ts
でDynamoDBの設定を追加します。
const serverlessConfiguration: AWS = {
provider: {
// DynamoDBアクセス設定
iamRoleStatements: [
{
Effect: "Allow",
// 許可する処理を設定
Action: ["dynamodb:PutItem", "dynamodb:Query"],
// 処理を許可するリソースを設定
Resource:
"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/sample_table",
},
],
},
// DynamoDBを設定
resources: {
Resources: {
DynamoDBTable: {
Type: "AWS::DynamoDB::Table",
Properties: {
// テーブル名
TableName: "sample_table",
// パーティションキー / ソートキーの型指定
AttributeDefinitions: [
{
AttributeName: "PK",
AttributeType: "S",
},
{
AttributeName: "SK",
AttributeType: "S",
},
],
// キーの種類指定(HASH=パーティションキー / RANGE=ソートキー)
KeySchema: [
{
KeyType: "HASH",
AttributeName: "PK",
},
{
KeyType: "RANGE",
AttributeName: "SK",
},
],
// プロビジョニングされたキャパシティの設定
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5,
},
},
},
},
},
// ...
};
これでデプロイを行うと「sample_table」というテーブルが作成されます。
デプロイが完了したら、AWS マネジメントコンソールのナビゲーションバー上の検索ボックスに「DynamoDB」と入力します。
サービスの中からDynamoDBが見つかるので、選択してDynamoDBダッシュボードを開きます。
ナビゲーションペインから テーブル を選択します。
DynamoDBにテーブルが作成されていることが確認できます。
これでDynamoDBのテーブル作成できるようになりました。
それでは実際にAWSマネジメントコンソール上からデータの登録と取得、削除を行ってみましょう。
ナビゲーションペインから テーブル > 項目を探索 を選択します。
テーブルの項目探索画面へ遷移するので、先ほど作成したテーブル sample_table
を選択します。
返された項目のエリア右上にある「項目を作成」ボタンを押下し、以下項目作成手順を進めてください。
DynamoDB項目作成手順
- 「項目を作成」画面右上から「JSONビュー」を選択
- 「DynamoDb JSONの表示」トグルをオフに切り替えます
- 登録するデータをJSON形式で入力し、「項目を作成」ボタンを押下
ここでは以下のデータを登録します。{ "PK": "ID:0001", "SK": "ShoppingList:01", "date": "20230820", "message": "minced meat", "quantity": "400g" }
DynamoDB項目取得手順
- テーブルの項目探索画面「項目のスキャンまたはクエリ」から「クエリ」を選択
- 「PK (パーティションキー)」に作成手順で登録したPK「ID:0001」を入力
- 「SK (ソートキー)」下のプルダウンから「次と等しい」を選択し、作成手順で登録したSK「ShoppingList:01」を入力
ここのプルダウンから任意のソート方法を選択することで、一致した項目等をまとめて取得することができます。 - 「実行する」ボタンを押下
- 「返された項目」に作成手順で登録したデータが表示されていることを確認してください
DynamoDB項目削除手順
- 削除したい項目の左側チェックボックスを選択
- 「返された項目」の右上「アクション」プルダウンから「項目を削除」を選択
- 本当に削除するか確認のポップアップが表示されるため、「削除」ボタンを押下
- 削除した項目のPK / SKで項目の探索を行い、ヒットしないことを確認してください
環境構築手順が完了しました!
APIを作ってみる
それでは実際にAPIの実装を行ってみましょう。
今回はDynamoDBへのデータの書き込みと、データの取得を行うAPIを作成するため、
TypeScriptテンプレートを使って作成したプロジェクトの hello
配下のディレクトリを参考に dbRegister
と dbRetrieve
というフォルダを functions
配下に作っていきます。
構成は以下のイメージです。
sample
└ src
└ functions
│ └ dbRegister
│ │ ├ handler.ts
│ │ └ index.ts
│ └ dbRetrieve
│ ├ handler.ts
│ └ index.ts
├ index.ts
└ schema.ts
sample/serverless.ts
に新規に作成するAPIのfunctionのパスを追加します。
import { dbRegister, dbRetrieve } from "./src/functions";
const serverlessConfiguration: AWS = {
// ...
// import the function via paths
functions: { dbRegister, dbRetrieve },
};
// DynamoDB登録API
export { dbRegister } from "./dbRegister";
// DynamoDB取得API
export { dbRetrieve } from "./dbRetrieve";
DynamoDBデータ登録API
まずはDynamoDBへデータの登録を行うAPIを作成します。
データはリクエスト本文で渡された値を登録します。
import type { ValidatedEventAPIGatewayProxyEvent } from "@libs/api-gateway";
import { formatJSONResponse } from "@libs/api-gateway";
import { middyfy } from "@libs/lambda";
import { dbRegisterSchema } from "@functions/schema";
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
const dynamo = new DynamoDBClient({ region: process.env.AWS_REGION });
const dbRegister: ValidatedEventAPIGatewayProxyEvent = async (event) => {
const todoTask = new PutItemCommand({
TableName: "sample_table",
Item: {
PK: { S: event.body.user_id },
SK: { S: event.body.task_id },
date: { S: event.body.date },
message: { S: event.body.message },
quantity: { S: event.body.quantity },
},
});
await dynamo.send(todoTask);
return formatJSONResponse({
statusCode: 200,
message: `${event.body.task_id}の書き込みに成功しました。`,
event,
});
};
export const main = middyfy(dbRegister);
import { dbRegisterSchema } from "@functions/schema";
import { handlerPath } from "@libs/handler-resolver";
export const dbRegister = {
handler: `${handlerPath(__dirname)}/handler.main`,
events: [
{
http: {
method: "post",
path: "dbRegister",
request: {
schemas: {
"application/json": dbRegisterSchema,
},
},
},
},
],
};
export const dbRegisterSchema = {
type: "object",
properties: {
user_id: { type: "string" },
task_id: { type: "string" },
date: { type: "string" },
message: { type: "string" },
quantity: { type: "string" },
},
required: ["user_id", "task_id", "date", "message", "quantity"],
} as const;
DynamoDBデータ取得API
続いて、DynamoDBからデータの取得を行うAPIを作成します。
ここでは検索条件をリクエスト本文で渡された値の「取得したいデータのPK」と、リクエスト本文で渡された値から始まる「取得したいデータのSK」とします。
import type { ValidatedEventAPIGatewayProxyEvent } from "@libs/api-gateway";
import { formatJSONResponse } from "@libs/api-gateway";
import { middyfy } from "@libs/lambda";
import { dbRetrieveSchema } from "@functions/schema";
import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
const dynamo = new DynamoDBClient({ region: process.env.AWS_REGION });
const dbRetrieve: ValidatedEventAPIGatewayProxyEvent = async (event) => {
const command = new QueryCommand({
TableName: "sample_table",
// 検索条件
KeyConditionExpression: "PK = :PK AND begins_with (SK, :SK )",
// 取得したい値のPK / SK
ExpressionAttributeValues: {
":PK": { S: event.body.user_id },
":SK": { S: event.body.task_id },
},
});
const returnToDoTask = await dynamo.send(command);
return formatJSONResponse({
statusCode: 200,
returnToDoTask: returnToDoTask.Items,
});
};
export const main = middyfy(dbRetrieve);
import { dbRetrieveSchema } from "@functions/schema";
import { handlerPath } from "@libs/handler-resolver";
export const dbRetrieve = {
handler: `${handlerPath(__dirname)}/handler.main`,
events: [
{
http: {
method: "post",
path: "dbRetrieve",
request: {
schemas: {
"application/json": dbRetrieveSchema,
},
},
},
},
],
};
export const dbRetrieveSchema = {
type: "object",
properties: {
user_id: { type: "string" },
task_id: { type: "string" },
},
required: ["user_id", "task_id"],
} as const;
これで DynamoDBへデータを登録するAPIと、DynamoDBからデータを取得するAPIが完成しました!
デプロイしてみる
それでは実際にデプロイしてみましょう!
手順はテンプレートで作成された hello という関数をデプロイした手順と同様に以下のコマンドを実行してください。
$ sls deploy --aws-profile sample-user --verbose
完了したら、AWSマネジメントコンソールのLambda関数を開いて dbRegister
と dbRetrieve
のLambdaが作られていることを確認してください。
テンプレートで作成された hello と同様に手作りのLambda関数がデプロイできました!
テストしてみる
それでは動作確認をするためにLambdaのテスト機能を使って実行してみます。
DynamoDBデータ登録API
まずはDynamoDBへデータの登録を行う dbRegister
をテストします。
手順はテンプレートで作成された hello という関数をLambdaでテストした手順と同様に、今度は dbRegister
を選択して進みます。
以下のJSONを「テスト」タブの「イベントJSON」フィールドへ入力し、「テスト」を実行します。
{
"headers": {
"Content-Type": "application/json"
},
"body": "{\"user_id\": \"ID:0001\",\"task_id\": \"ShoppingList:01\",\"date\": \"20230820\",\"message\": \"potato\",\"quantity\": \"150g\"}"
}
テストが成功したログを確認したら、DynamoDBのテーブルを見に行きましょう。
手動でDynamoDBの項目作成したものを取得した手順と同様に、以下クエリを入力して実行します。
dbRegister
で登録したデータが表示されていることを確認してください。これで dbRegister
の機能でDynamoDBへデータの登録が正常に行われていることが確認できました!
DynamoDBデータ取得API
続いて、DynamoDBからデータの取得を行う dbRetrieve
をテストします。
今度は dbRetrieve
を選択して進みます。
以下のJSONを「テスト」タブの「イベントJSON」フィールドへ入力し、「テスト」を実行します。
{
"headers": {
"Content-Type": "application/json"
},
"body": "{\"user_id\": \"ID:0001\",\"task_id\": \"ShoppingList:01\"}"
}
テストが成功したログを確認し、先ほど dbRegister
で登録したデータが返却されていることを確認してください。
これで dbRetrieve
の機能でDynamoDBからデータの取得が正常に行われていることが確認できました!
さいごに
以上で本記事は終了です。
今回はAWSサービスのLambdaとDynamoDBに触れ、サーバレスの環境構築とDynamoDBへデータの読み書きを行う簡単なAPIの実装とデプロイを行なって、AWSマネジメントコンソール上からLambdaのテスト実行やDynamoDBのクエリ実行ができるようになりました。
今後の展望としては、Reactで画面を作って今回のAPIを画面操作で呼べるようにしたり、他のAWSサービスと組み合わせて面白い機能の実装ができればと思います。