DynamoDB Query Examples: A Comprehensive Guide for Developers in 2025
DynamoDB’s Query operation is one of the most powerful and cost-effective ways to retrieve data from your tables. Unlike the Scan operation, which reads every item in a table, Query allows you to efficiently retrieve items by primary key values, enabling fast and economical access to your data.
This guide provides a comprehensive collection of practical DynamoDB Query examples across multiple interfaces (AWS CLI, Python Boto3, and Node.js), covering both basic usage and advanced techniques to help you master this essential operation.
When to Use Query vs Scan
Before diving into examples, let’s clarify when to use Query:
- Use Query when: You need to find items based on primary key values (partition key and optional sort key conditions)
- Use Scan when: You need to search across all items without knowing their key values (though this should be avoided for large tables)
Query operations are typically more efficient because:
- They consume fewer read capacity units (RCUs)
- They return results faster, especially in large tables
- They reduce your DynamoDB costs
Note: A well-designed DynamoDB table should support your common access patterns through Query operations rather than Scans. If you find yourself needing frequent Scans, consider reviewing your table design.
Dynomate: Modern DynamoDB GUI Client
Built for real developer workflows with AWS profile integration, multi-session support, and team collaboration.
No account needed. Install and start using immediately.
- Table browsing across regions
- Flexible query & scan interface
- AWS API logging & debugging
Basic Query Syntax
At its core, a Query operation requires:
- The table name
- A partition key value (equal condition only)
- Optional sort key conditions (equal, less than, greater than, begins with, etc.)
Let’s explore how to implement queries across different interfaces.
AWS CLI Query Examples
The AWS Command Line Interface (CLI) provides a straightforward way to query DynamoDB tables.
Basic Query by Partition Key
Assume we have a table named Orders
with a partition key CustomerID
:
aws dynamodb query \
--table-name Orders \
--key-condition-expression "CustomerID = :customerId" \
--expression-attribute-values '{":customerId": {"S": "CUSTOMER123"}}'
This query retrieves all orders for customer “CUSTOMER123”.
Query with Sort Key Condition
If our table has a composite key (partition key + sort key), we can add conditions on the sort key:
aws dynamodb query \
--table-name Orders \
--key-condition-expression "CustomerID = :customerId AND OrderDate > :date" \
--expression-attribute-values '{
":customerId": {"S": "CUSTOMER123"},
":date": {"S": "2025-01-01"}
}'
This retrieves all orders for CUSTOMER123 placed after January 1, 2025.
Using Different Sort Key Comparisons
DynamoDB supports various comparison operators for sort keys:
# "begins_with" example - find orders with tracking numbers starting with "TX"
aws dynamodb query \
--table-name Orders \
--key-condition-expression "CustomerID = :customerId AND begins_with(TrackingNumber, :prefix)" \
--expression-attribute-values '{
":customerId": {"S": "CUSTOMER123"},
":prefix": {"S": "TX"}
}'
# "between" example - find orders in a date range
aws dynamodb query \
--table-name Orders \
--key-condition-expression "CustomerID = :customerId AND OrderDate BETWEEN :start AND :end" \
--expression-attribute-values '{
":customerId": {"S": "CUSTOMER123"},
":start": {"S": "2025-01-01"},
":end": {"S": "2025-03-31"}
}'
Python Boto3 Query Examples
Python’s Boto3 library provides a more programmatic way to interact with DynamoDB.
Basic Query
import boto3
from boto3.dynamodb.conditions import Key
# Initialize DynamoDB resource
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Orders')
# Query for a specific customer
response = table.query(
KeyConditionExpression=Key('CustomerID').eq('CUSTOMER123')
)
# Print the items returned
for item in response['Items']:
print(item)
Query with Sort Key Conditions
from boto3.dynamodb.conditions import Key, Attr
# Query with a date range on the sort key
response = table.query(
KeyConditionExpression=Key('CustomerID').eq('CUSTOMER123') &
Key('OrderDate').between('2025-01-01', '2025-03-31')
)
# Query with begins_with on sort key
response = table.query(
KeyConditionExpression=Key('CustomerID').eq('CUSTOMER123') &
Key('OrderDate').begins_with('2025-03')
)
Filtering Query Results
While Query operations filter by key conditions, you can further refine results with a FilterExpression:
# Query for a customer's orders, but only return completed ones
response = table.query(
KeyConditionExpression=Key('CustomerID').eq('CUSTOMER123'),
FilterExpression=Attr('OrderStatus').eq('COMPLETED')
)
Important: FilterExpression is applied after DynamoDB retrieves items matching the KeyConditionExpression. This means you’re still charged for reading all items that match the key conditions, even if they’re filtered out afterward. For this reason, it’s best to design your data model to minimize the need for filtering.
Node.js Query Examples
For JavaScript developers, the AWS SDK for JavaScript provides similar capabilities.
Basic Query
const AWS = require('aws-sdk');
// Configure AWS SDK
AWS.config.update({ region: 'us-east-1' });
const docClient = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: 'Orders',
KeyConditionExpression: 'CustomerID = :customerId',
ExpressionAttributeValues: {
':customerId': 'CUSTOMER123'
}
};
docClient.query(params, (err, data) => {
if (err) {
console.error('Error:', err);
} else {
console.log('Query results:', data.Items);
}
});
Using Promises and Async/Await
A more modern approach using async/await:
async function queryCustomerOrders(customerId, startDate, endDate) {
const params = {
TableName: 'Orders',
KeyConditionExpression: 'CustomerID = :customerId AND OrderDate BETWEEN :start AND :end',
ExpressionAttributeValues: {
':customerId': customerId,
':start': startDate,
':end': endDate
}
};
try {
const data = await docClient.query(params).promise();
return data.Items;
} catch (err) {
console.error('Error querying orders:', err);
throw err;
}
}
// Usage
queryCustomerOrders('CUSTOMER123', '2025-01-01', '2025-03-31')
.then(orders => console.log('Orders:', orders))
.catch(err => console.error('Failed:', err));
Querying Secondary Indexes
One of DynamoDB’s most powerful features is the ability to create secondary indexes that enable efficient queries on non-key attributes.
Querying a Global Secondary Index (GSI)
Assume we have a GSI named ProductIndex
with partition key ProductID
:
AWS CLI
aws dynamodb query \
--table-name Orders \
--index-name ProductIndex \
--key-condition-expression "ProductID = :productId" \
--expression-attribute-values '{":productId": {"S": "PRODUCT456"}}'
Python
response = table.query(
IndexName='ProductIndex',
KeyConditionExpression=Key('ProductID').eq('PRODUCT456')
)
Node.js
const params = {
TableName: 'Orders',
IndexName: 'ProductIndex',
KeyConditionExpression: 'ProductID = :productId',
ExpressionAttributeValues: {
':productId': 'PRODUCT456'
}
};
docClient.query(params, (err, data) => {
if (err) console.error(err);
else console.log(data.Items);
});
Handling Pagination
DynamoDB limits the response size of a single Query operation to 1MB. For queries that return more data, you’ll need to handle pagination.
Using LastEvaluatedKey for Pagination
Python
import boto3
from boto3.dynamodb.conditions import Key
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Orders')
def query_all_customer_orders(customer_id):
items = []
last_key = None
while True:
if last_key:
response = table.query(
KeyConditionExpression=Key('CustomerID').eq(customer_id),
ExclusiveStartKey=last_key
)
else:
response = table.query(
KeyConditionExpression=Key('CustomerID').eq(customer_id)
)
items.extend(response['Items'])
last_key = response.get('LastEvaluatedKey')
if not last_key:
break
return items
# Get all orders for a customer, handling pagination automatically
all_orders = query_all_customer_orders('CUSTOMER123')
print(f"Retrieved {len(all_orders)} orders")
Node.js
async function queryAllItems(params) {
let items = [];
let lastEvaluatedKey = undefined;
do {
// Add the ExclusiveStartKey if we have a LastEvaluatedKey from a previous query
if (lastEvaluatedKey) {
params.ExclusiveStartKey = lastEvaluatedKey;
}
const response = await docClient.query(params).promise();
// Add the items from this page to our collection
items = items.concat(response.Items);
// Get the LastEvaluatedKey for the next query, if any
lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);
return items;
}
// Usage example
const params = {
TableName: 'Orders',
KeyConditionExpression: 'CustomerID = :customerId',
ExpressionAttributeValues: {
':customerId': 'CUSTOMER123'
}
};
queryAllItems(params)
.then(allItems => console.log(`Retrieved ${allItems.length} items`))
.catch(err => console.error('Query failed:', err));
Familiar with these Dynamodb Challenges ?
- Writing one‑off scripts for simple DynamoDB operations
- Constantly switching between AWS profiles and regions
- Sharing and managing database operations with your team
You should try Dynomate GUI Client for DynamoDB
- Create collections of operations that work together like scripts
- Seamless integration with AWS SSO and profile switching
- Local‑first design with Git‑friendly sharing for team collaboration
Limiting Results and Projecting Attributes
You can optimize your queries by requesting only the attributes you need and limiting the number of results.
AWS CLI with Projection and Limit
aws dynamodb query \
--table-name Orders \
--key-condition-expression "CustomerID = :customerId" \
--expression-attribute-values '{":customerId": {"S": "CUSTOMER123"}}' \
--projection-expression "OrderID, OrderDate, TotalAmount" \
--limit 10
Python with Projection and Limit
response = table.query(
KeyConditionExpression=Key('CustomerID').eq('CUSTOMER123'),
ProjectionExpression='OrderID, OrderDate, TotalAmount',
Limit=10
)
Node.js with Projection and Limit
const params = {
TableName: 'Orders',
KeyConditionExpression: 'CustomerID = :customerId',
ExpressionAttributeValues: {
':customerId': 'CUSTOMER123'
},
ProjectionExpression: 'OrderID, OrderDate, TotalAmount',
Limit: 10
};
docClient.query(params, (err, data) => {
if (err) console.error(err);
else console.log(data.Items);
});
Query With Consistent Reads
By default, DynamoDB Query operations use eventually consistent reads. For applications requiring the most up-to-date data, you can use strongly consistent reads at the cost of additional read capacity consumption.
AWS CLI
aws dynamodb query \
--table-name Orders \
--key-condition-expression "CustomerID = :customerId" \
--expression-attribute-values '{":customerId": {"S": "CUSTOMER123"}}' \
--consistent-read
Python
response = table.query(
KeyConditionExpression=Key('CustomerID').eq('CUSTOMER123'),
ConsistentRead=True
)
Node.js
const params = {
TableName: 'Orders',
KeyConditionExpression: 'CustomerID = :customerId',
ExpressionAttributeValues: {
':customerId': 'CUSTOMER123'
},
ConsistentRead: true
};
docClient.query(params, (err, data) => {
if (err) console.error(err);
else console.log(data.Items);
});
Query Best Practices
To get the most out of DynamoDB Query operations:
- Design for your access patterns: Structure your table and indexes to support the queries your application needs
- Minimize attribute projections: Only request the attributes you need to reduce consumed read capacity
- Use sparse indexes: If you’re frequently querying for a subset of items, consider a sparse GSI where only relevant items have the indexed attribute
- Monitor consumption: Keep an eye on consumed capacity using the ReturnConsumedCapacity parameter
- Prefer Query over Scan: Whenever possible, design your data model to use Query instead of Scan operations
- Be careful with FilterExpressions: Remember that filtering happens after items are read, so you’re still charged for the read capacity
Common Query Issues and Solutions
Issue: Query returns no items
- Verify the partition key value is correct
- Check if you’re querying the correct table or index
- Ensure your sort key condition isn’t overly restrictive
- Verify the attribute names in your expressions match your table schema
Issue: Query is too slow
- Consider adding a Global Secondary Index to better support your query pattern
- Reduce the amount of data retrieved by using projections
- Ensure you’re not using a FilterExpression when you could use a better index
Issue: Query is expensive (high consumed capacity)
- Check if you’re using a strongly consistent read when eventual consistency would suffice
- Ensure you’re only requesting attributes you need
- Consider using a GSI with specific projected attributes
Issue: 1MB result limit is being hit
- Implement pagination using the LastEvaluatedKey mechanism
- Consider more selective key conditions
Switching from Dynobase? Try Dynomate
Developers are switching to Dynomate for these key advantages:
Better Multi-Profile Support
- Native AWS SSO integration
- Seamless profile switching
- Multiple accounts in a single view
Developer-Focused Workflow
- Script-like operation collections
- Chain data between operations
- Full AWS API logging for debugging
Team Collaboration
- Git-friendly collection sharing
- No account required for installation
- Local-first data storage for privacy
Privacy & Security
- No account creation required
- 100% local data storage
- No telemetry or usage tracking
Conclusion
DynamoDB’s Query operation provides a powerful way to efficiently retrieve data from your tables. By following the examples and best practices in this guide, you can optimize your queries for performance, cost, and scalability.
Remember that effective DynamoDB queries start with proper table design. Always consider your access patterns when designing your schema and indexes to ensure you can retrieve data efficiently.
For more complex DynamoDB operations, consider using Dynomate, which offers an intuitive interface for constructing, testing, and optimizing your queries without having to write code. Dynomate makes it easier to visualize your data, understand query performance, and refine your DynamoDB implementation for maximum efficiency.
Need to learn more about DynamoDB? Check out our other guides: