DynamoDB UpdateItem in Node.js (AWS SDK v3)
The low-level v3 client speaks DynamoDB JSON in both directions, which means every number you send and every number you get back is a string. That is not a wart; it is the only way a 38-digit DynamoDB number survives a language whose only number type is a double. It is also where the bugs are.
Code
import {DynamoDBClient, UpdateItemCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const command = new UpdateItemCommand({
TableName: 'Music',
Key: {
Artist: {S: 'Arturo Sandoval'},
SongTitle: {S: 'Cubano Chant'}
},
UpdateExpression: 'SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2',
ExpressionAttributeNames: {
'#upd0': 'Genre',
'#upd1': 'Year',
'#upd2': 'Awards'
},
ExpressionAttributeValues: {
':updValue0': {S: 'Latin Jazz'},
':updValue1': {N: '1994'},
':updValue2': {N: '1'}
},
ReturnValues: 'ALL_NEW'
});
const response = await client.send(command);
console.log(response.Attributes); // the item after the updateAgainst an item that had no Genre and no Awards, response.Attributes comes back as:
{"Artist":{"S":"Arturo Sandoval"},"Awards":{"N":"1"},"Genre":{"S":"Latin Jazz"},"Year":{"N":"1994"},"SongTitle":{"S":"Cubano Chant"}}typeof response.Attributes.Awards.N is "string", so response.Attributes.Awards.N + 1 evaluates to "11". Nothing throws, nothing warns, and the wrong number goes into your next write. Parse at the boundary: Number(response.Attributes.Awards.N).
Explanation
The expression is a plain string, and v3 will not check it.
UpdateItemCommandvalidates the shape of the input object, never the grammar insideUpdateExpression, so a typo is a round trip and a 400. The grammar is in update expressions;ADD #upd2 :updValue2is the atomic increment, and addingConditionExpression: 'attribute_exists(Artist)'makes the call update-only rather than an upsert.ReturnValues: 'UPDATED_NEW'is usually the one you want. The same update returns{"Awards":{"N":"2"}}and nothing else.ALL_NEWships the whole item back on every call, which on a fat item is bandwidth you are paying for to read one counter.$metadatais v3's out-of-band channel:{"httpStatusCode":200,"requestId":"...","attempts":1,"totalRetryDelay":0}.attemptsis the honest answer to "did this retry", which matters when you are reasoning about whether a non-idempotent write ran twice.ValidationExceptionis not a class you can catch, only anameyou can compare. A missing alias comes back aserr.name === 'ValidationException'witherr.messageset toInvalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: Year.The document client is the other trade.
@aws-sdk/lib-dynamodbtakes native JS values and unmarshalls the response, at the cost of that string safety.marshall({awards: 9007199254740993})from@aws-sdk/util-dynamodbrefuses outright:Number 9007199254740992 is greater than Number.MAX_SAFE_INTEGER. Use NumberValue from @aws-sdk/lib-dynamodb.Look closely at the number in that message. It ends in
2, not the3that was written in the literal: JavaScript had already rounded it before the SDK ever saw it. The low-level client in this snippet cannot have that problem, because{N: '9007199254740993'}is text all the way to the wire.
What a failed condition hands you
Add ReturnValuesOnConditionCheckFailure: 'ALL_OLD' to the input and the thrown error carries the item that beat you:
name: ConditionalCheckFailedException | message: "The conditional request failed" | http: 400
err.Item: {"Artist":{"S":"Arturo Sandoval"},"Awards":{"N":"2"},"Year":{"N":"1994"},"SongTitle":{"S":"Cubano Chant"},"Genre":{"S":"Latin Jazz"}}err.Item is raw DynamoDB JSON regardless of which client threw it, and it is free. Without it, the honest way to find out why an optimistic-concurrency update failed is a follow-up GetItem that costs a read and may already be stale again.
The DynamoDB JSON converter turns that payload into a plain JS object and back, which is the fastest way to build a fixture from a real item. To pull that item off a live table in the first place, download DynoTable.
Related guides
- DynamoDB update expressions —
SET,ADD,REMOVE,DELETE, and idioms. - Understanding ReturnValues — what each
ReturnValuesoption gives you. - "Attribute name is a reserved keyword" — why the alias map here isn't optional.
- "Invalid UpdateExpression" syntax errors — the common SET/ADD syntax mistakes, decoded.
References
- UpdateItem — Amazon DynamoDB API Reference
- UpdateItemCommand — AWS SDK for JavaScript v3 Reference
- Update expressions — Amazon DynamoDB Developer Guide
Last verified 2026-07-28 against the official AWS documentation linked above.