Pagination in DynamoDB
DynamoDB never returns "all" results in one call. A Query or Scan returns at
most 1 MB of data, then hands you a LastEvaluatedKey to resume from. Getting
pagination right means looping on that key — not on a counter.
The loop
let key;
do {
const out = await client.send(new QueryCommand({...params, ExclusiveStartKey: key}));
process(out.Items);
key = out.LastEvaluatedKey;
} while (key);When LastEvaluatedKey is undefined, you've reached the end. Pass it back as
ExclusiveStartKey to fetch the next slice.
The control flow is a single loop that exits only on an absent key:
Every pass either resumes from the returned key or stops — there is no counter.
Limit is not a page size
Limit caps how many items DynamoDB evaluates, not how many it returns after
a FilterExpression. A Limit: 25 query behind a filter can return 3 items and
still hand you a LastEvaluatedKey — you must keep paging until the key is empty,
even when a page looks short. A non-empty LastEvaluatedKey never promises
more matching items either; only an absent key proves you've reached the end.
Let the SDK paginate
Both SDKs wrap the loop above so you can iterate pages directly:
// AWS SDK for JavaScript v3
import {paginateQuery} from '@aws-sdk/lib-dynamodb';
for await (const page of paginateQuery({client}, params)) {
process(page.Items);
}# boto3
paginator = client.get_paginator('query')
for page in paginator.paginate(**params):
process(page['Items'])No page numbers
DynamoDB has no total count and no random page access — you can't jump to
"page 7" or page backwards without replaying the cursors. Design UIs around
infinite scroll / "load more", not numbered pages. (A Select: 'COUNT' query
still reads — and bills for — every matched item to count them.)
Stateless cursors for APIs
LastEvaluatedKey is just the key attributes of the last item. Base64-encode it
and hand it to clients as an opaque nextToken; decode it back into
ExclusiveStartKey on the next request. No server-side cursor state.
That token is DynamoDB-JSON — eyeball or hand-craft one with the DynamoDB-JSON converter. And if you're paging to work around a Scan, that's usually a signal to add an index instead.
Try DynoTable to page through query results visually, with the cursor tracked for you.