DynamoDB Cheat Sheet
DynamoDB is a key-value and document store where access patterns drive table design: partition key (PK), optional sort key (SK), and GSIs for alternate query paths.
How to use this DynamoDB cheat sheet GetItem fetches one item by full primary key; Query reads items sharing a partition key with optional SK conditions. Scan reads entire table—avoid in production except migrations or analytics with filters.
AWS SDK v3 uses command objects (GetItemCommand, QueryCommand) with DynamoDBClient. On-demand vs provisioned capacity affects cost; single-table design colocates related entities with composite keys and overloaded GSIs.
Quick DynamoDB example import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, GetCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
const client = DynamoDBDocumentClient.from(new DynamoDBClient({ region: 'us-east-1' }));
const user = await client.send(new GetCommand({
TableName: 'App',
Key: { PK: 'USER#123', SK: 'PROFILE' },
}));
const orders = await client.send(new QueryCommand({
TableName: 'App',
KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
ExpressionAttributeValues: { ':pk': 'USER#123', ':sk': 'ORDER#' },
})); PK/SK & Access Patterns Concept Example Notes Partition key PK: "USER#123" Hash key; spreads load across partitions Sort key SK: "ORDER#2024-01-01" Range key; enables begins_with queries Composite key { PK: "USER#1", SK: "PROFILE" } Uniquely identifies one item Single-table PK=USER#1, SK=ORDER#5 same table as profiles Multiple entity types via key prefixes Hot partition High write rate to one PK Salting or write sharding spreads load Item size limit 400 KB per item Split large blobs to S3 with pointer attribute TTL ttl: epochSeconds Automatic expiry without billing for storage after delete ConditionExpression attribute_not_exists(PK) Optimistic locking and idempotent writes
GetItem, Query & Scan Operation Example When to use GetItem GetCommand({ Key: { PK, SK } }) Single item by full primary key Query KeyConditionExpression: "PK = :pk" All items under one partition Query + SK PK = :pk AND SK BETWEEN :a AND :b Range within partition begins_with begins_with(SK, :prefix) Prefix scan within partition only FilterExpression FilterExpression: "amount > :min" Applied after read; still consumes RCUs Scan ScanCommand({ TableName }) Full table read—expensive, slow BatchGetItem Keys: [{ PK, SK }, ...] Up to 100 items; 16 MB response limit TransactWrite TransactWriteCommand All-or-nothing up to 100 actions
GSI & SDK v3 Commands Feature Example Notes GSI GSI1PK, GSI1SK on same item Alternate query access pattern Sparse GSI Only items with GSI keys appear Index subset without filter waste Projection ProjectionType: "INCLUDE" Copy only needed attributes to GSI PutItem PutCommand({ Item: { PK, SK, ...attrs } }) Create or replace entire item UpdateItem UpdateExpression: "SET #s = :val" Partial update with ExpressionAttributeNames DeleteItem DeleteCommand({ Key }) Remove single item DocumentClient DynamoDBDocumentClient.from(client) Native JS types instead of AttributeValue maps marshall/unmarshall marshall({ foo: "bar" }) Low-level AttributeValue conversion
Capacity & Single-Table Patterns Topic Example Notes On-demand BillingMode: "PAY_PER_REQUEST" Pay per request; good for variable traffic Provisioned ReadCapacityUnits, WriteCapacityUnits Predictable cost at steady load RCU/WCU 1 RCU = 4 KB strongly consistent read Eventually consistent reads half RCU Entity collection PK=USER#1, SK=PROFILE|ORDER#|INVOICE# Query all user data with PK only Adjacency list Multiple SK types under one PK Graph-like one-hop relationships Inverted index GSI GSI1PK=ORDER#5, GSI1SK=USER#1 Lookup orders by order id Write sharding PK=USER#1#SHARD@@CODE0@@#123;hash % 10} Reduce hot partition on writes Idempotency ConditionExpression: attribute_not_exists(PK) Prevent duplicate event processing
UpdateItem with optimistic locking await client.send(new UpdateCommand({
TableName: 'App',
Key: { PK: 'USER#1', SK: 'PROFILE' },
UpdateExpression: 'SET #v = :newV, #version = :next',
ConditionExpression: '#version = :current',
ExpressionAttributeNames: { '#v': 'name', '#version': 'version' },
ExpressionAttributeValues: { ':newV': 'Ada', ':next': 2, ':current': 1 },
})); ConditionExpression fails with ConditionalCheckFailedException if version mismatch.
Batch write with retry unprocessed import { BatchWriteCommand } from '@aws-sdk/lib-dynamodb';
let request = { RequestItems: { App: puts } };
do {
const res = await client.send(new BatchWriteCommand(request));
request = { RequestItems: res.UnprocessedItems };
} while (Object.keys(request.RequestItems).length); Exponential backoff on UnprocessedItems for throttling.
Single-table entity types // Same table, different entities
{ PK: 'USER#42', SK: 'PROFILE', name: 'Ada', type: 'User' }
{ PK: 'USER#42', SK: 'ORDER#1001', total: 99, type: 'Order' }
{ PK: 'ORDER#1001', SK: 'METADATA', status: 'SHIPPED', type: 'OrderMeta' } Design keys for every query before creating the table—GSIs are costly to change later.
Common mistakes Using Scan where Query with a designed PK would suffice. FilterExpression thinking it reduces RCUs—it filters after the read. Creating a GSI for every ad-hoc query instead of modeling access patterns upfront. Storing large blobs in DynamoDB instead of S3 with a pointer attribute. Key takeaways Design PK/SK and GSIs from access patterns, not from relational table shapes. Prefer Query and GetItem; treat Scan as a last resort. Use DocumentClient and SDK v3 commands for cleaner Node.js code. Single-table design with prefixed keys keeps related data queryable in one round trip.
Pro Tip
Sketch every read/write your app needs on paper first—changing keys after launch requires painful migrations and GSI rebuilds.
Common DynamoDB Mistakes Go to next item