AWS SDK for JavaScript v3 への移行
こんにちは。開発 2 部 箕浦です。
サーバーレスの開発には欠かせないAWS Lambdaですが、ランタイムバージョンのサポートが切れると、
AWSはセキュリティパッチ等を適用しなくなり、サポート切れの関数はテクニカルサポートの対象ではなくなります。
さらに、新規作成や更新もできなくなるので、ランタイムのバージョンアップが必要になってきます。
今回はとある案件で、構築時はNode.js 16.xで作成していたLambda関数 でしたが、
Node.js 16.x の 2024 年 6 月 12 日のサポート終了に伴い、Node.js 20.x へ移行いたしました。
その際、AWS SDKのバージョンもv2からv3へ移行する必要がありました。
v3自体は、Node.js 18.x から登場し、利用可能だったようですが、16のまま放置していたので、今回初めて移行を試みました。
今更な情報かもしれませんが、その際、いろいろソースコードを変更する箇所がありましたので、一部紹介します。
今回SDKにて利用しているサービスは、Lambda、S3、Cognito、DynamoDB、SES でしたのでこれらをピックアップします。
ポイントとしては、
- ①インポート
- ②呼び出し
- ③エラーハンドリング
の3か所です。
インポート
従来は以下のように aws-sdk
自体をインポートして、それぞれのサービスのクライアントを生成する必要がありました。
const aws = require('aws-sdk');
const documentClient = new aws.DynamoDB.DocumentClient();
v3 では以下のように個別のモジュールをインポートする必要があります。 面倒ですが、SDKをまるごとインポートするよりも個別にインポートした方がサイズを削減することが可能です。
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, DeleteCommand } = require("@aws-sdk/lib-dynamodb");
const documentClient = DynamoDBDocumentClient.from(new DynamoDBClient({ region: 'ap-northeast-1' }));
cognito, lambda, s3, ses の場合は、
const {
CognitoIdentityProviderClient,
AdminCreateUserCommand,
AdminDeleteUserCommand,
} = require("@aws-sdk/client-cognito-identity-provider");
const { LambdaClient, InvokeCommand } = require("@aws-sdk/client-lambda");
const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3");
const { SESClient, SendEmailCommand } = require("@aws-sdk/client-ses");
const cognito = new CognitoIdentityProviderClient({});
const lambda = new LambdaClient({});
const s3 = new S3Client({});
const ses = new SESClient({ region: 'ap-northeast-1' });
このような感じです。
呼び出し
v2では、各サービスのAPIの呼び出し方は、一般的には クライアントが提供しているAPIに対応しているメソッドにパラメータを直接渡す方法でした。
const params = {
TableName: TABLE_NAME,
Key: {
UserName: user_id,
},
};
await documentClient.delete(params).promise();
v3 では、APIに対応しているCommandにパラメータを渡してコマンドを生成して、 それをクライアントのsendメソッドに渡してやります。
const params = {
TableName: TABLE_NAME,
Key: {
UserName: user_id,
},
};
await documentClient.send(new DeleteCommand(params));
さらにLambdaの場合は、戻り値のPayloadのParseの部分にも修正が必要でした
const ret = await lambda.invoke(params).promise();
const result = JSON.parse(ret.Payload);
const ret = await lambda.send(new InvokeCommand(params));
const result = JSON.parse(Buffer.from(ret.Payload).toString());
エラーハンドリング
v2 では、以下のように、例外が発生した際に、catchに渡される例外オブジェクトのcodeメンバを参照することで、
エラーハンドリングが可能でしたが、v3ではこの記述はだめでした。
try {
const ret = await cognito.send(new AdminCreateUserCommand(params));
} catch (e) {
if (e.code === 'UsernameExistsException') {
response.statusCode = 400;
body.resultCode = 'E0007';
} else if (e.code === 'InvalidPasswordException') {
response.statusCode = 400;
body.resultCode = 'E0016';
} else {
response.statusCode = 500;
body.resultCode = 'E0001';
}
response.body = JSON.stringify(body);
callback(null, response);
return;
}
v3では以下のように、instanceof を使ってハンドリングする必要がありました。
try {
const ret = await cognito.send(new AdminCreateUserCommand(params));
} catch (e) {
if (e instanceof UsernameExistsException) {
response.statusCode = 400;
body.resultCode = 'E0007';
} else if (e instanceof InvalidPasswordException) {
response.statusCode = 400;
body.resultCode = 'E0016';
} else {
response.statusCode = 500;
body.resultCode = 'E0001';
}
response.body = JSON.stringify(body);
callback(null, response);
return;
}
今回取り上げたサービスはほんの一部ですが、それ以外のサービスでは他にも変更点はある可能性はあります。
もし、同じ境遇の方いましたら、ご参考までに。