DynamoDB BatchGetItem in Node.js (AWS SDK v3)
BatchGetItem fetches up to 100 items by primary key in one request. In AWS SDK v3 the call has to be a loop, because UnprocessedKeys arrives on a successful response rather than an error. What fills it, and why 16 MB and 1 MB-per-partition are the numbers that matter, is covered in batch operations in DynamoDB. This page is about the v3 call and what it hands back.
Code
import {BatchGetItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let requestItems = {
Music: {
Keys: [
{Artist: {S: 'Arturo Sandoval'}, SongTitle: {S: 'Cubano Chant'}},
{Artist: {S: 'Arturo Sandoval'}, SongTitle: {S: 'A Mis Abuelos'}},
{Artist: {S: 'Ella Fitzgerald'}, SongTitle: {S: 'Misty'}}
]
}
};
const items = [];
let attempt = 0;
do {
const response = await client.send(new BatchGetItemCommand({RequestItems: requestItems}));
items.push(...(response.Responses?.Music ?? []));
// A partial result is NOT an error: throttling, a >16 MB response, or an
// internal failure returns the leftovers in UnprocessedKeys. Retry them
// with exponential backoff.
requestItems = response.UnprocessedKeys;
if (requestItems && Object.keys(requestItems).length > 0) {
attempt += 1;
await sleep(Math.min(100 * 2 ** attempt, 5000));
}
} while (requestItems && Object.keys(requestItems).length > 0);
console.log(`Fetched ${items.length} items`);Explanation
- The optional chaining is not defensive noise.
ResponsesandUnprocessedKeysare both optional in the v3 types, soresponse.Responses?.Music ?? []and theObject.keys()guard are what the compiler asks for. In plain JavaScript they are what stops the first empty response from throwing. - Branch on
err.name. v3 puts the service error code there, and the two failures this command actually raises are not retryable, so they must never fall into the backoff loop. Over 100 keys givesValidationException/Too many items requested for the BatchGetItem call; the same key twice givesProvided list of item keys contains duplicates. Both are reproduced verbatim on the Python page. UnprocessedKeysarrives already shaped asRequestItems, which is the only reason the loop can assign it straight back. It is not a pagination cursor and it does not mean the call failed.- The backoff is AWS's instruction, not a nicety. The API reference tells you to use "an exponential backoff algorithm" because an immediate retry lands on the same throttled partition.
ConsistentReadandProjectionExpressionare per table, set inside eachRequestItemsentry rather than at the top level. That is easy to miss when the map has one key and looks like a flat request.
What the response actually comes back as
Run the fence above against DynamoDB Local 3.3.0 with all three songs present, add ReturnConsumedCapacity: 'TOTAL', and log the song titles instead of the count:
order: [ 'A Mis Abuelos', 'Misty', 'Cubano Chant' ]
UnprocessedKeys: {}
ConsumedCapacity: [ { TableName: 'Music', CapacityUnits: 1.5 } ]The request listed Cubano Chant, A Mis Abuelos, Misty. The response is in none of those positions, which is why the loop pushes into a flat array instead of indexing by offset. Match items back to requests on their key attributes, and include those keys in any ProjectionExpression so you still have something to match on.
Set ConsistentRead: true on that same Music entry and the same three keys cost 3 units instead of 1.5. Three items under 4 KB each are billed as three separate GetItem reads, at half a unit eventually consistent and a full unit strongly consistent. The pricing calculator converts that per-item arithmetic into a monthly number before you commit to a read pattern.
Now delete Misty and re-run: two items, an empty UnprocessedKeys, and 1.0 units. The missing key was billed nothing. That is a DynamoDB Local artifact and not the contract; the BatchGetItem reference (fetched 2026-07-28) says requests for nonexistent items consume the minimum read capacity for the read type. Do not size a batch of cache misses from a local run.
To pull a set of keys back and look at what actually returned, without writing the loop first, download DynoTable.
Related examples
- DynamoDB BatchGetItem in Python — the same batch read with boto3.
- DynamoDB BatchGetItem with the AWS CLI — the same batch read from the shell.
- DynamoDB GetItem in Node.js — the single-item read this batches.
- Batch operations in DynamoDB — limits, partial failure, and when batching pays off.
- "Too many items requested for the BatchGetItem call" — more than 100 keys in one request.
- "Provided list of item keys contains duplicates" — the same key twice in one batch.
References
- BatchGetItem — Amazon DynamoDB API Reference
- Error handling with DynamoDB — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Last verified 2026-07-28 against the official AWS documentation linked above.