Get All Items from DynamoDB in Node.js (AWS SDK v3)
Reading a whole table in SDK v3 means paginating a Scan to the end. Each response caps at 1 MB, and you feed LastEvaluatedKey back as ExclusiveStartKey until one stops coming back (how DynamoDB cursors work).
The loop below is written by hand so the cursor is visible. In real code you would reach for paginateScan, which the SDK already ships.
Code
import {DynamoDBClient, ScanCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
const items = [];
let lastEvaluatedKey;
do {
const response = await client.send(
new ScanCommand({
TableName: 'Music',
ExclusiveStartKey: lastEvaluatedKey
})
);
items.push(...(response.Items ?? []));
lastEvaluatedKey = response.LastEvaluatedKey;
} while (lastEvaluatedKey);
console.log(`Table holds ${items.length} items`);Explanation
paginateScanalready does this —import {paginateScan} from '@aws-sdk/client-dynamodb', thenfor await (const page of paginateScan({client}, {TableName: 'Music'})). It is generated from the same three cursor fields the loop above uses by hand (ExclusiveStartKey,LastEvaluatedKey,Limit), so nothing changes behaviour when you switch.- Only an absent
LastEvaluatedKeymeans done — a response with zeroItemsand a cursor is normal, not an empty table. Breaking onItems.length === 0is the classic missing-rows bug, and aFilterExpressionmakes empty pages routine rather than rare. Limitcounts items evaluated per call, not items total — it is the third field the paginator drives, and neither it nor the paginator'spageSizebounds the size of the array you end up holding.ExclusiveStartKey: undefinedon the first pass is fine — the serializer drops undefined members, so the first iteration needs no special case.itemsgrows to the size of the table — process each page inside the loop and let it go (write, stream, aggregate) unless you know the table is small. Accumulating is one table growth away from an out-of-memory crash.- A scan bills every byte it reads, on every run —
ProjectionExpressionshrinks what crosses the wire and not the bill (why), and aFilterExpressiondrops items after they are read and charged (Scan with a filter). On a hot path you want a Query. - Split it with
Segment/TotalSegments— N async workers, each driving its own cursor over its own slice, all on one shared client. Node will happily run them concurrently; the read cost does not change, only the wall clock (when that is worth it).
Do it visually
The DynamoDB query builder generates this entire program, pagination loop included, from a form, in SDK v3 and seven other targets.
DynoTable pages through a live table for you in an infinite-scrolling grid, and exports the Scan behind that grid as runnable code. Download DynoTable.
Related examples
- Get all items in Python — the same full read with boto3's paginator.
- Get all items with the AWS CLI — the CLI pages for you.
- DynamoDB Scan in Node.js — scanning with a
FilterExpression. - Parallel scans — Segment/TotalSegments, worker counts, and when to bother.
- Why is my DynamoDB Scan slow and expensive? — the cost model and how to avoid it.
- DynamoDB ProvisionedThroughputExceededException — reading the whole table is the classic way to hit it.
- "The provided starting key is invalid" — a mangled resume key in the pagination loop.
References
- Scan — Amazon DynamoDB API Reference
- Scanning tables in DynamoDB — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
- [
paginateScan— AWS SDK for JavaScript v3 source](https://github.com/aws/aws-sdk-js-v3/blob/main/the client-dynamodb ScanPaginator module)
Last verified 2026-07-28 against the official AWS documentation linked above.