AWS Lambda(node.js v18)+ Serverless Framework

Serverless frameworkでAWS Lambda node.v18を動かしたときのメモです。

LambdaをNode.jsのバージョン18で動かそうとすると、AWS SDK for JavaScript v3を使用する必要があります。LambdaでNode v16以前のコードを読み込むとaws-sdkがデフォルトでは読み込まれていないためエラーになります。このAWS SDK for JavaScript v3はv2から大幅に仕様が異なっていて正直しんどい...

そこで、よく使用するS3とDynamoDBの操作をまとめました。

Node.js v18の環境で、aws-sdkを読み込んだときの結果

2023-01-07T02:30:18.629Z	undefined	ERROR	Uncaught Exception 	
{
    "errorType": "Runtime.ImportModuleError",
    "errorMessage": "Error: Cannot find module 'aws-sdk'\nRequire stack:\n- /var/task/src/functions/v0_monitoring_download/handler.js\n- /var/runtime/index.mjs",
    "stack": [
        "Runtime.ImportModuleError: Error: Cannot find module 'aws-sdk'",
        "Require stack:",
        "- /var/task/src/functions/v0_monitoring_download/handler.js",
        "- /var/runtime/index.mjs",
        "    at _loadUserApp (file:///var/runtime/index.mjs:1000:17)",
        "    at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1035:21)",
        "    at async start (file:///var/runtime/index.mjs:1200:23)",
        "    at async file:///var/runtime/index.mjs:1206:1"
    ]
}

今回は、ローカルでの開発環境構築と、DynamoDBとS3を動かすまでを対応してみます。まずは、必要なライブラリをインストールします。なお、AWSのリソースに対してローカルからはSSO(Single Sign-On)を利用しているので、@aws-sdk/credential-providersもインストールしています。

npm i -D @aws-sdk/credential-providers
npm i -D @aws-sdk/client-dynamodb
npm i -D @aws-sdk/lib-dynamodb
npm i -D @aws-sdk/client-s3

認証部分

AWS_SDK_LOAD_CONFIG=1のときはローカル動作させるためにprofileを指定しています。

それ以外はlambdaで動作させることを想定してcredentialsをundefinedにしています。

export const config = {
  region: 'ap-northeast-1',
  credentials: process.env.AWS_SDK_LOAD_CONFIG === '1' ? fromSSO({ profile: 'your-profile' }) : undefined,
}

S3

S3に保存しているのがJSON形式の文字列なので、このようにJSONオブジェクトにしました。v2からはコマンド体型以外にBodyのデータを文字列化するためのコマンドが違っています。res.Body.toString()では文字列化出来ませんでした。

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
const client = new S3Client(config);
const command = new GetObjectCommand({
  Bucket: S3_CLIENT_BUCKET,
  Key: S3_KEY,
});
client.send(command).then((res) => res.Body.transformToString()).then((values) => resolve(JSON.parse(values))).catch((err) => {
  console.log(err);
  reject(err);
})

DynamoDB

v2からコマンド体型がは変わりましたが、使用するパラメータには差が無いようです。
エラーはerr.codeにエラーコードが入っていましたが、v3ではerr.nameにエラーコードが入っているようです。

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { QueryCommand, PutCommand, QueryCommandInput } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient(config);
const command = new QueryCommand({
  TableName: DbName,
  KeyConditionExpression: '#type = :type AND #timestamp < :from',
  ExpressionAttributeNames: {
    "#type": "type",
    "#timestamp": "timestamp",
  },
  ExpressionAttributeValues: {
    ":type": PRIMARY_KEY,
    ":from": SECONDRY_KEY
  }
});
client.send(command).then((values) => {
  console.log(values);
}).catch((err)=>{
  if (err.name === 'ProvisionedThroughputExceededException') {
    // ProvisionedThroughputExceededExceptionの例外処理
  } else {
    // ProvisionedThroughputExceededException 以外の例外処理
  }
});

vscodeの設定

Serverless frameworkを使用して、mock.jsonに記載したS3 Triggerのイベントをローカルで擬似的に発火してテストするためのvscodeの設定になります。


    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "v0_monitorig_download",
            "program": "${workspaceFolder}/node_modules/.bin/sls",
            "cwd": "${workspaceFolder}",
            "request": "launch",
            "args": [
                "invoke",
                "local",
                "-f",
                "handler",
                "--path",
                "src/functions/handler/mock.json",
                "--aws-profile",
                "profile",
            ],
            "skipFiles": [
                "<node_internals>/**"
            ],
            "type": "node"
        },
    ]
}