はじめに
参考
ハマりポイント
- Lambda関数からRDS Proxyに接続するには、LambdaをVPC内に設置する必要がある
- Lambda関数のサブネット、セキュリティグループをRDS Proxyと合わせる必要がある
- VPC内のLambda関数からSecrets Managerに接続するにはVPCエンドポイントかインターネットアクセスが必要になる
- PrismaとProstgreSQLのテーブル名やENUMタイプ名は大文字・小文字や複数形まで名前をあわせる必要がある
- Lambda関数のpermissionを設定する実行ロールには、EC2, CloudWatch, SecretsManagerの権限を付与し、Trust relationshipでは、LambdaとRDSサービスのassumeRoleの設定をする
- Lambdaのタイムアウトとメモリがデフォルトでは3秒と128MBと小さいので、適切に増やす
- PostgreSQLでは、datasouce urlでschemaまで指定する
- PostgreSQLのTimestampカラムには、JavaScriptのDateをISO 8601形式に変換してから設定する
チュートリアル
アプリ作成
npm init
npm install -D @types/aws-lambda esbuild
index.ts作成
import { Context, APIGatewayProxyResult, APIGatewayEvent } from 'aws-lambda';
export const handler = async (event: APIGatewayEvent, context: Context): Promise<APIGatewayProxyResult> => {
console.log(`Event: ${JSON.stringify(event, null, 2)}`);
console.log(`Context: ${JSON.stringify(context, null, 2)}`);
return {
statusCode: 200,
body: JSON.stringify({
message: 'hello world',
}),
};
};
package.jsonの編集
{
"name": "lambda-api",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prebuild": "rm -rf dist && mkdir -p dist/node_modules",
"build": "esbuild index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outfile=dist/index.js",
"postbuild": "cp -r node_modules/.prisma dist/node_modules/.prisma && cd dist && zip -r index.zip *"
},
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@types/aws-lambda": "^8.10.143",
"esbuild": "^0.23.0"
}
}
ビルド
実行ロールの作成
Lambda関数の作成
aws lambda create-function \
--function-name hello-world \
--runtime "nodejs20.x" \
--role 実行ロールのARN \
--zip-file "fileb://dist/index.zip" \
--handler index.handler
開発
prismaコマンドのインストール
パッケージインストール
npm install @prisma/client
npm install aws-sdk
schema.prisma
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "rhel-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATASOURCE_URL")
}
enum meeting_type {
google
zoom
}
enum meeting_state {
init
done
canceled
}
model meetings {
id Int @id @default(autoincrement())
order_id Int
meeting_type meeting_type
created_at DateTime
updated_at DateTime
state meeting_state
schedule DateTime
link String?
}
index.ts
import { Context, APIGatewayProxyResult, APIGatewayEvent } from 'aws-lambda';
import { PrismaClient, meeting_state, meeting_type } from '@prisma/client'
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
export const handler = async (event: APIGatewayEvent, context: Context): Promise<APIGatewayProxyResult> => {
const secretName = process.env.SECRET_NAME_DB_CRED;
const secret = await secretsManager.getSecretValue({ SecretId: secretName as string }).promise();
const dbCredentials = JSON.parse(secret.SecretString ?? '{}');
const databaseUrl = `postgresql://${dbCredentials.username}:${dbCredentials.password}@${process.env.DATASOURCE_URL}:5432/${process.env.DB_NAME}?schema=orders`;
console.log(databaseUrl);
const prisma = new PrismaClient({
datasources: {
db: {
url: databaseUrl,
},
},
});
// console.log(prisma)
const date = new Date();
const isoString = date.toISOString();
const user = await prisma.meetings.create({
data: {
order_id: 1,
meeting_type: meeting_type.google,
state: meeting_state.init,
schedule: isoString,
link: 'aaa',
created_at: isoString,
updated_at: isoString
},
})
return {
statusCode: 200,
body: JSON.stringify({
user: user
}),
};
};
ビルド&デプロイ
package.json
"scripts": {
"prebuild": "rm -rf dist && mkdir dist && mkdir dist/node_modules",
"build": "esbuild index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outfile=dist/index.js",
"postbuild": "cp -r node_modules/.prisma dist/node_modules/.prisma && cd dist && zip -r index.zip *"
},
node_modules/.prisma/client, node_modules/@prisma/clientコード生成
ビルド
Lambdaコードの更新
aws lambda update-function-code \
--function-name hello-world \
--zip-file "fileb://dist/index.zip"
Lambdaの環境変数
- DATASOURCE_URL
- DB_NAME
- SECRET_NAME_DB_CRED
Lambdaの実行ロール
- Permission Policy
- EC2
- CloudWatch
- SecretsManager
- Trust relationship
- LambdaサービスのassumeRole
- RDSサービスのassumeRole