Node.js (AWS SDK v3) での DynamoDB PutItem
PutItem はアイテム 1 件をまるごと書き込み、同じプライマリキーの既存アイテムがあれば置き換えます(UpdateItem との違いはアイテム単位のアクションが扱います)。v3 のクライアントは DynamoDB JSON を直接送るので、Item には素の JavaScript ではなく { S: … } / { N: … } の値が入ります。
コード
import {DynamoDBClient, PutItemCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const command = new PutItemCommand({
TableName: 'Music',
Item: {
Artist: {S: 'Arturo Sandoval'},
SongTitle: {S: 'Cubano Chant'},
AlbumTitle: {S: 'Danzon'},
Year: {N: '1994'},
Awards: {N: '0'}
},
ConditionExpression: 'attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)',
ExpressionAttributeNames: {
'#cond0': 'Artist',
'#cond1': 'SongTitle'
}
});
try {
await client.send(command);
console.log('Song written');
} catch (err) {
if (err.name === 'ConditionalCheckFailedException') {
console.log('A song with that key already exists — not overwritten');
} else {
throw err;
}
}解説
チェックすべきは err.name であり、エラーに載っているのはそれだけではありません。上の条件失敗を捕捉してオブジェクトを表示すると、こうなりました。
err.name ConditionalCheckFailedException
err instanceof Error true
err.message The conditional request failed
err.$metadata.httpStatusCode 400v3 のエラーはすべて、ステータスコード、リクエスト ID、試行回数を持つ $metadata を伴います。ログ 1 行に欲しいのはまさにそれです。err.name はモジュール化されたパッケージをまたいで安定しています。instanceof ConditionalCheckFailedException も動きますが、クラスを値として import することになるので、バンドラーがそれを残します。
エラーは、書き込みを阻んだアイテムを渡してくれます。コマンドに ReturnValuesOnConditionCheckFailure: 'ALL_OLD' を足すと err.Item が中身入りで届きます。上の実行では 5 つの属性が入り、Year は {"N":"1994"} でした。作成専用のハンドラーの多くは、失敗の後に GetItem を打ってすでに何があったのかを調べます。その往復は避けられます。(ReturnValues: 'ALL_OLD' は成功パス側の従兄弟です。残りは ReturnValues が扱います。)
marshall() は思っている以上に多くの入力を拒みます。このページの型付き Item を DynamoDBDocumentClient と素のオブジェクトに置き換えるのはよくある次の一手ですが、@aws-sdk/util-dynamodb はデフォルトで厳格です。実際に起きた 3 つのスロー、そのままの逐語です。
{Genre: undefined} Pass options.removeUndefinedValues=true to remove undefined values from map/array/set.
{tags: new Set()} Pass a non-empty set, or options.convertEmptyValues=true.
{n: 9007199254740993} Number 9007199254740992 is greater than Number.MAX_SAFE_INTEGER. Use NumberValue from @aws-sdk/lib-dynamodb.本番に届くのは 1 つ目です。存在しないのではなく undefined であるオプショナルなフィールドがマーシャル時にスローするので、DynamoDBDocumentClient.from(client, {marshallOptions}) に removeUndefinedValues: true を入れるのが定番の対処です。
3 行目をもう一度読んでください。リテラルは 9007199254740993 でしたが、メッセージは 9007199254740992 を引用しています。SDK が値を見る前に JavaScript がすでに丸めていたので、SDK は受け取ったものを報告しているのです。DynamoDB が N を文字列として運ぶ理由は、まさにこれです。DynamoDB は 38 桁の精度を保持し、JS の number が保持するのは 15〜17 桁です。実体が識別子であるものは S に、実体が小数であるものは NumberValue か自分で整形した文字列に置きましょう。
ConditionExpression は、否と言うときにも書き込みキャパシティを使います。AWS 曰く: "if the expression evaluates to false, DynamoDB still consumes write capacity units from the table"(2026-07-28 取得)。作成専用のリトライをタイトループで回すと、試行ごとに課金されます。目安として、約 15 KB のアイテムの put が成功したときは "CapacityUnits": 15 と報告されました。書き込みは、読み取りが使う 4 KB ではなく 1 KB 単位で切り上げられます。
エイリアスは飾りではありません。#cond0/#cond1 は ExpressionAttributeNames を通じて Artist/SongTitle に解決されます。名前を直接書く書き方は、どれかが予約語と衝突するまでは動き、衝突した途端、触ってもいない属性のせいで式が失敗します。
ビジュアルに行う
上のマーシャリングのルールは、両方の形を並べて見るのが一番確かめやすいです。無料の DynamoDB JSON コンバーターは、素の JSON を型付きの { S: … } 形式に、またその逆に変換するので、送る前に marshall() が何を作るはずだったのかを確認できます。
自分のテーブルに対してアイテムを書いたり編集したりするには(属性ごとのフォーム、型ピッカー、結果を SDK v3 のコードとしてコピー)、DynoTable をダウンロードしてください。
関連ガイド
- DynamoDB の条件式 —
attribute_not_exists、楽観的ロックなど。 - DynamoDB のデータ型 — 各属性型の書き方。
- DynamoDB ConditionalCheckFailedException — アイテムがすでに存在するとき、作成専用の条件が何を投げるか。
- DynamoDB ValidationException — 不正なアイテムや式に対する何でも屋のエラー。
参考資料
- PutItem — Amazon DynamoDB API Reference
- PutItemCommand — AWS SDK for JavaScript v3 Reference
- Capacity unit consumption — Amazon DynamoDB Developer Guide
- Condition expressions — Amazon DynamoDB Developer Guide
- Working with items and attributes — Amazon DynamoDB Developer Guide
2026-07-28 に Node v24.18.0、@aws-sdk/client-dynamodb 3.1095.0、@aws-sdk/util-dynamodb 3.996.7 で、ポート 9000 の DynamoDB Local(amazon/dynamodb-local)に対して再現しました。エラー文字列、オブジェクトの形、キャパシティの数値は捕捉した出力で、そのままの逐語です。