What you'll learn
- Placeholders starting with
:live inExpressionAttributeValuesand stand in for actual data values. - Placeholders starting with
#live inExpressionAttributeNamesand stand in for attribute names. - You must alias a name with
#whenever the attribute's name is a DynamoDB reserved word (likeStatus,Name, orType) or contains special characters such as.or-.
Section 01
Values: the ':' placeholders
You never paste raw data directly into an expression string. Instead you write a placeholder like :atk and bind the real value separately in ExpressionAttributeValues. DynamoDB substitutes it safely at runtime — this prevents injection-style mistakes and lets the Document Client convert each value to the correct DynamoDB type.
The expression references :atk; the actual number 120 is bound on the side. You can change the value without ever editing the expression string. (Attack isn't a reserved word, so it's safe to use directly — more on that next.)
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({ region: "ap-south-1" });
const docClient = DynamoDBDocumentClient.from(client);
await docClient.send(new UpdateCommand({
TableName: "Pokemon",
Key: { Name: "Mew" },
UpdateExpression: "SET Attack = :atk",
ExpressionAttributeValues: {
":atk": 120,
},
}));Section 02
Names: the '#' placeholders & reserved words
DynamoDB reserves hundreds of words — Status, Name, Type, Comment, and many more. If one of your attributes is named after a reserved word, using it directly in an expression throws a validation error.
The fix is an ExpressionAttributeName: a placeholder starting with # that stands in for the attribute name itself.
| Attribute name | Reserved? | How to reference it |
|---|---|---|
| Attack | No | Use directly: SET Attack = :v |
| Status | Yes | Alias it: SET #s = :v |
| Name | Yes | Alias it: attribute_exists(#n) |
When in doubt, alias the name — using # always works, reserved or not.
Ready · 3 steps · Orbi
This animation plays when you scroll to it. Press Play to start it now.
All steps as text
- Your app sends UpdateItem to the Pokemon table with Name = "Mew". UpdateExpression "SET Status = :status"
- DynamoDB hashes "Mew" to 416a585c, which routes the request to partition P0.
- DynamoDB responds with ValidationException: Status is a reserved keyword, so the expression is rejected before anything is written.
Section 03
Putting them together
Most real expressions use both at once: # for the attribute name and : for its value.
Read it as: “Set the attribute #status (which is really Status) to the value :status (which is "Champion").”
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({ region: "ap-south-1" });
const docClient = DynamoDBDocumentClient.from(client);
await docClient.send(new UpdateCommand({
TableName: "Pokemon",
Key: { Name: "Mew" },
UpdateExpression: "SET #status = :status",
ExpressionAttributeNames: {
"#status": "Status",
},
ExpressionAttributeValues: {
":status": "Champion",
},
}));Practice
Your turn
Status attribute — but Status is a DynamoDB reserved word, so it can't go straight into the expression. Alias it with a # name placeholder and bind the new value with a : value placeholder.UpdateExpression: "SET #status = :status", map "#status" to "Status" in ExpressionAttributeNames, and bind :status to "Champion" in ExpressionAttributeValues.Check
Check your understanding
Quick Quiz · one question, as many tries as you need.
Status — a DynamoDB reserved word. How do you safely set it in an UpdateExpression?Choose one answer.