DynamoDB: GSIs, Streams & Interview Questions
Global Secondary Indexes (GSI)
GSIs let you query on non-primary-key attributes. Each GSI has its own PK and optional SK, and maintains its own copy of the data — eventually consistent by default.
// Table: PK=USER#id, SK=ORDER#date
// Add GSI to query all orders by status (cross-user)
// GSI: PK=status, SK=createdAt
// Query the GSI
const { Items } = await db.send(new QueryCommand({
TableName: TABLE,
IndexName: 'GSI1', // GSI name set during table creation
KeyConditionExpression: 'GSI1PK = :status AND GSI1SK > :since',
ExpressionAttributeValues: {
':status': 'ORDER_STATUS#pending',
':since': '2026-01-01',
},
}))Up to 20 GSIs per table (Local Secondary Indexes also available — same PK, different SK)
GSI consumes its own RCU/WCU — provision separately or use on-demand
Sparse indexes: only items with the GSI key attributes are indexed — useful for filtering
GSI overloading: reuse the same GSI for multiple entity types by putting entity-type prefixes in GSI keys
DynamoDB Streams
Streams capture a time-ordered sequence of item-level changes (INSERT, MODIFY, REMOVE). Use them to trigger Lambda functions, replicate data, or build event-driven systems.
// Enable on table: StreamSpecification = KEYS_ONLY | NEW_IMAGE | OLD_IMAGE | NEW_AND_OLD_IMAGES
// Lambda trigger automatically invoked with stream records
export const handler = async (event: DynamoDBStreamEvent) => {
for (const record of event.Records) {
if (record.eventName === 'INSERT') {
const newItem = record.dynamodb?.NewImage
// newItem attributes are in DynamoDB AttributeValue format
// Use unmarshall from @aws-sdk/util-dynamodb to convert
}
if (record.eventName === 'MODIFY') {
const old = record.dynamodb?.OldImage
const updated = record.dynamodb?.NewImage
}
if (record.eventName === 'REMOVE') {
const deleted = record.dynamodb?.OldImage
}
}
}DynamoDB Local (Development)
# Run DynamoDB locally via Docker
docker run -p 8000:8000 amazon/dynamodb-local
# Point SDK to local instance
const client = new DynamoDBClient({
region: 'local',
endpoint: 'http://localhost:8000',
credentials: { accessKeyId: 'fake', secretAccessKey: 'fake' }
})
# CLI with local
aws dynamodb list-tables --endpoint-url http://localhost:8000 --region us-east-1Interview Questions
When to choose DynamoDB over PostgreSQL? Known access patterns, massive scale (millions of requests/sec), serverless/event-driven architecture. Avoid when: complex queries, ad-hoc reporting, many-to-many joins.
What is a hot partition and how do you avoid it? Single partition receiving too many requests. Fix: add random suffix to PK (write sharding), use higher-cardinality PKs, cache hot reads in ElastiCache.
Difference between Query and Scan? Query uses the index (efficient, O(results)); Scan reads the entire table (expensive, avoid). Always prefer Query with proper key design.
What is the maximum item size in DynamoDB? 400KB. For larger items, store in S3 and save the S3 key in DynamoDB.
How does single-table design differ from multi-table? Single-table stores all entities in one table with generic PK/SK — enables fetching related data in one Query. Multi-table is easier to reason about but requires multiple queries for related data.
How do you handle DynamoDB transactions? TransactWrite/TransactGet (up to 100 items, 4MB) — 2x cost. For high-volume: optimistic locking with ConditionExpression instead.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free