AWS SDK for JavaScript v3でDynamoDB JSONと通常のJSONの間でデータ構造の変換を行う ~ lib-dynamodb ~

2022.05.22

いわさです。

先日、AWS SDK for JavaScriptでDynamoDBClientで項目をスキャンした際に、DynamoDBによるデータ型記述子を、util-dynamodbを使ってアンマーシャリングする方法を紹介しました。

前回は、util-dynamodbを使って明示的に変換を行っていました。
他の方法として、lib-dynamodbを使うと透過的に通常のJSON形式でオブジェクトを取得することが出来ます。(内部はutil-dynamodbを使用しています)

client-dynamodb

おさらいですが、通常のDynamoDBClientを使ったスキャン処理は以下のような形になります。

hoge.js

const { DynamoDBClient, ScanCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({
    region: "ap-northeast-1"
});
const command = new ScanCommand({
    TableName: "hoge"
});
const run = async() => {
    const response = await client.send(command);
    response.Items.forEach(function(item){
        console.log(item)
    })
}
run();
$ node hoge.js
{ id: { S: '2' }, num1: { N: '111' }, str1: { S: 'hoge2' } }
{ id: { S: '1' }, num1: { N: '222' }, str1: { S: 'hoge1' } }

DynamoDBのデータ型記述子が追加されていますね。

lib-dynamodbのコマンドを使う

lib-dynamodbではDynamoDBDocumentClientなど、データ型記述子などのDynamoDBの仕様を抽象化して通常のJSONのように扱うためのライブラリです。
主に、前回の記事でご紹介したマーシャリング/アンマーシャリングの部分を担うコマンド群や、変換時のオプションを設定するためのDynamoDBDocumentClientが含まれています。

本日はこれらを導入して使ってみたいと思います。

$ npm install @aws-sdk/lib-dynamodb
npm WARN 0509dynamo@1.0.0 No description
npm WARN 0509dynamo@1.0.0 No repository field.

+ @aws-sdk/lib-dynamodb@3.87.0
added 2 packages from 1 contributor and audited 76 packages in 4.638s

2 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

   ╭────────────────────────────────────────────────────────────────╮
   │                                                                │
   │      New major version of npm available! 6.14.10 → 8.10.0      │
   │   Changelog: https://github.com/npm/cli/releases/tag/v8.10.0   │
   │               Run npm install -g npm to update!                │
   │                                                                │
   ╰────────────────────────────────────────────────────────────────╯

まずは、スキャンコマンドをclient-dynamodbからlib-dynamodbのものに変換してみます。
いくつかのコマンドはそのまま使えるのですが、パラメータや名称の互換がないコマンドもあったので、個別のリファレンスを確認のうえご利用ください。

const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { ScanCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({
    region: "ap-northeast-1"
});
const command = new ScanCommand({
    TableName: "hoge"
});
const run = async() => {
    const response = await client.send(command);
    response.Items.forEach(function(item){
        console.log(item)
    })
}
run();
$ node hoge.js
{ id: '2', num1: 111, str1: 'hoge2' }
{ id: '1', num1: 222, str1: 'hoge1' }

後述するDynamoDBDocumentClientを使うとプレーンなJSONで利用出来るという記述をWeb上でいくつか見かけましたが、私が検証した限りではコマンドをlib-dynamodbのものを利用することが必要でした。

DynamoDBDocumentClientで変換オプションを指定出来る

次に、DynamoDBDocumentClientを使ってみます。
こちらのクライアントを使ったうえで先程のコマンドを実行するとマーシャリング/アンマーシャリング時の変換オプションを制御することが出来ます。
ここではアンマーシャリング時に数値フィールドをJavaScriptの数値ではなくて文字列として変換するように指定します。

const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { unmarshall, marshall } = require("@aws-sdk/util-dynamodb");
const { DynamoDBDocumentClient, ScanCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({
    region: "ap-northeast-1"
});
const command = new ScanCommand({
    TableName: "hoge"
});
const marshallOptions = {
};
const unmarshallOptions = {
    wrapNumbers: false, 
};
const translateConfig = { marshallOptions, unmarshallOptions };

const run = async() => {
    const response = await DynamoDBDocumentClient.from(client, translateConfig).send(command);
    console.log(response.DynamoDBDocumentClientResolvedConfig)
    response.Items.forEach(function(item){
        console.log(item)
    })
}
run();
# wrapNumbers: true
$ node hoge.js
{ id: '2', num1: { value: '111' }, str1: 'hoge2' }
{ id: '1', num1: { value: '222' }, str1: 'hoge1' }

# wrapNumbers: false
$ node hoge.js
{ id: '2', num1: 111, str1: 'hoge2' }
{ id: '1', num1: 222, str1: 'hoge1' }

デフォルトではJavaScript上も数値として変換されますが、キャストする際に問題が出る際に安全にフィールドを利用することが出来ます。

さいごに

本日は、先日の記事を補足する形で、lib-dynamodbを使ったDynamoDBオブジェクトのマーシャリング/アンマーシャリングについて紹介しました。
util-dynamodbを明示的に利用する際はアイテム毎に個別に処理してやる必要がありましたが、今回はコマンドレベルで適用すれば済むので変換を意識せずに抽象化して利用することが出来ます。
DynamoDBの低レイヤーのオブジェクト情報が必要なければ、lib-dynamodbを利用することが推奨されています。