Node.js(AWS SDK v3)中的 DynamoDB PutItem
PutItem 會寫入一整個項目,並取代任何具有相同 primary key 的既有項目(以項目為單位的操作談的是它與 UpdateItem 的差別)。v3 用戶端直接送出 DynamoDB JSON,所以 Item 裝的是 { S: … } / { N: … } 這類值,而不是單純的 JavaScript 值。
程式碼
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 400每一個 v3 錯誤都帶著 $metadata,內含狀態碼、請求 id 與嘗試次數,那正是你想寫進 log 的東西。err.name 在各個模組化套件之間都是穩定的;instanceof ConditionalCheckFailedException 也可行,但會把那個類別當成值匯入,於是打包器會把它留著。
錯誤可以直接把擋下這次寫入的那個項目交給你。在指令中加上 ReturnValuesOnConditionCheckFailure: 'ALL_OLD',err.Item 就會有內容:在上面那次執行中是五個屬性,其中 Year 是 {"N":"1994"}。多數僅建立的處理常式會在失敗後再做一次 GetItem,好知道原本那裡是什麼。那趟來回是可以省掉的。(ReturnValues: 'ALL_OLD' 是成功路徑上的表親;ReturnValues 談了其餘部分。)
marshall() 拒絕的輸入比你以為的多。把本頁這種有型別標記的 Item 換成 DynamoDBDocumentClient 加上單純物件,通常是下一步,而 @aws-sdk/util-dynamodb 預設是嚴格的。三個真實的錯誤,逐字照錄:
{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.第一個是會一路跑到正式環境的那一個:一個值為 undefined(而非根本不存在)的選用欄位會在 marshal 時拋錯,而標準修法是在 DynamoDBDocumentClient.from(client, {marshallOptions}) 裡設 removeUndefinedValues: true。
請把第三行再讀一次。字面值寫的是 9007199254740993;訊息引用的卻是 9007199254740992。JavaScript 在 SDK 看到它之前就已經把這個值四捨五入了,所以 SDK 回報的是它收到的東西。這正是 DynamoDB 以字串傳輸 N 的全部理由:它能保有 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 的項目回報 "CapacityUnits": 15;寫入是以每 1 KB 進位,而不是讀取用的 4 KB。
那些別名是有承重作用的。#cond0/#cond1 會透過 ExpressionAttributeNames 解析成 Artist/SongTitle。直接內嵌名稱在其中一個撞上保留字之前都沒問題,而一旦撞上,運算式就會因為一個你根本沒動到的屬性而失敗。
改用視覺化操作
上面那些 marshal 規則,最容易的檢查方式是把兩種形式並排看。免費的 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)重現。錯誤字串、物件形狀與容量讀數皆為擷取到的輸出,逐字照錄。