serverless.tsで書くAWS Lambda + Node.js + express
前置き
ServerlessFrameworkの設定ファイルをymlでなく、tsで書いたのでその備忘録です。
AWSに以下のコンポーネントを作成しています。
- S3
- IAM
- Lambda
- API Gateway
- Cloud Watch Log Group
環境
- macOS BigSur 11.6.2
- npm v8.0.0
- npx v8.0.0
- node v14.x
- Visual Studio Code 1.65.2
準備
AWS Lambda + Node.js + TypeScript
のテンプレート作成コマンドを実行(参考:公式ドキュメント)
npx serverless create --template aws-nodejs-typescript --path serverless-sample
serverless-sample
ディレクトリが作成されるので、こちらのディレクトリに移動
cd serverless-template
すでにpackage.json
が用意されているので、ライブラリをインストールする
npm i
AWSのアクセスキー・シークレットキーを用意し、以下のコマンドを実行
aws configure
以下の入力を求められるので、適当な値を入力
AWS Access Key ID [None]: <取得したアクセスキー>
AWS Secret Access Key [None]: <取得したシークレットアクセスキー>
Default region name [None]: <任意のリージョン>(例:ap-northeast-1)
Default output format [None]: <結果出力形式>(例:json)
試しにテンプレをデプロイする
npx sls deploy -r ap-northeast-1
結果に出力されるエンドポイントに向けてcurlを実行する
curl --location --request POST '{出力されたエンドポイント}' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Frederic"
}'
長めのレスポンスがjson
で返ってくる
{
"message": "Hello Frederic, welcome to the exciting Serverless world!",
"event": {
"resource": "/hello",
/* 中略 */
"rawBody": "{\n \"name\": \"Frederic\"\n}"
}
}
S3
S3を見るとデプロイ用の素材を収めたバケットが生成されている。
バケット名をカスタマイズしたいので、serverless.ts
を編集する
provider: {
deploymentBucket: { name: 'serverless-sample-bucket' }
}
上記の記載だけではバケットを作成してくれないので、以下のプラグインを使用する
serverless-deployment-bucket
plugins: ['serverless-esbuild', 'serverless-deployment-bucket'],
また、以下のようにパッケージをインストールする
npm install serverless-deployment-bucket --save-dev
上記全てを実施した上でデプロイするとdeploymetBucket
プロパティで定義した名前のバケットが生成される
IAMロール
デフォルトでは以下のIAMロール・ポリシーが作成されている
- serverless-sample-dev-ap-northeast-1-lambdaRole
- serverless-sample-dev-lambda
- logs:CreateLogStream
- logs:CreateLogGroup
- logs:PutLogEvents
上記の内容を参考に自前でlambdaにアタッチするIAMロールを作成する
resources: {
Resources: {
ServerlessSampleLambdaRole: {
Type: 'AWS::IAM::Role',
Properties: {
RoleName: 'serverless-sample-lambda-role',
AssumeRolePolicyDocument: {
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: {
Service: ['lambda.amazonaws.com']
},
Action: ['sts:AssumeRole']
}
]
},
ManagedPolicyArns: [
'arn:aws:iam::${aws:accountId}:policy/serverless-sample-lambda-managed-policy'
]
},
DependsOn: ['ServerlessSampleLambdaManagedPolicy']
},
ServerlessSampleLambdaManagedPolicy: {
Type: 'AWS::IAM::ManagedPolicy',
Properties: {
ManagedPolicyName: 'serverless-sample-lambda-managed-policy',
PolicyDocument: {
Version: '2012-10-17',
Statement: [
{
Sid: 'log',
Effect: 'Allow',
Action: ['logs:CreateLogStream', 'logs:CreateLogGroup', 'logs:PutLogEvents'],
Resource: 'arn:aws:logs:${aws:region}:${aws:accountId}:log-group:/aws/lambda/serverless-sample-${sls:stage}'
}
]
}
}
}
}
}
resources
プロパティ以下にAWSCloudFormation
のテンプレート構文を記載して作成していきます。(詳細はこちら)
lambdaで使用するiamの定義。roleにはテンプレート構文で定義したプロパティ名を使用可能
provider: {
iam: {
role: 'ServerlessSampleLambdaRole'
}
}
デプロイするとIAMロールとポリシーが作成され、lambdaに作成したロールがアタッチされている
Lambda + API Gateway
GETリクエストでjsonを返すシンプルなlambdaを作成する。
まず、functions
プロパティで以下を設定する
-
名前
: nameプロパティで指定 -
ハンドラー
: handlerプロパティで指定 -
API Gatewayのパス
: eventsプロパティで指定- lambda内でパスのハンドリングを行うので、API Gatewayは全てのリクエストを通す設定とする
functions: {
app: {
name: '${sls:stage}-serveless-sample-lambda',
handler: 'src/app.handler',
events: [
{
http: {
method: 'ANY',
path: '{proxy+}'
}
}
]
}
}
続いてhandler
で指定した階層(srcディレクトリ直下)に下記内容のapp.ts
ファイルを配置する
'use strict'
import serverlessExpress from '@vendia/serverless-express'
import express from 'express'
const app = express()
app.get('/test', (_req, res) => {
res.json({ sample: "serverless" });
res.end();
})
export const handler = serverlessExpress({ app })
さらに、以下のnpmパッケージをインストールする
以上を実施し、デプロイコマンドを実行
API Gatewayができているので、ステージエディタのGETメソッドを開いたページに表示されているURLへGETリクエストする。({proxy+}の部分はapp.tsで設定したパスtest
に変換)
curl https://{ランダム生成された値}.execute-api.ap-northeast-1.amazonaws.com/dev/test
以下のレスポンスが返ってくれば成功!
{"sample":"serverless"}
CloudWatchLogs
現時点で/aws/lambda/dev-serveless-sample-lambda
というロググループが作成されている
自動で生成されたロググループを上書きする場合、serverless.ts
のresources
階層にextensions
プロパティを追加する -> 公式資料はこちら
以下は、ロググループの有効期限を上書きする例
resources: {
extensions: {
AppLogGroup: {
Properties: {
RetentionInDays: 1
}
}
}
}
Discussion