NOTE: This pagination library only works on indexes with a range key.
Usage
Compatible with AWS SDK v2 and v3
import { DynamoDB } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
import { getPaginatedResult, decodeCursor } from 'dynamodb-paginator';
interface User {
id: string
name: string
}
const documentClient = DynamoDBDocument.from(new DynamoDB({}));
const limit = 25;
const standardQueryParams = {
TableName: 'Users',
Limit: limit,
KeyConditionExpression: 'id = :id',
ExpressionAttributeValues: {
':id': '1'
}
};
// Could be a cursor from a previous paginated result
const cursor = undefined;
const paginationParams = decodeCursor(cursor) || standardQueryParams;
const result = await documentClient.query(paginationParams);
// By default the cursors are encoded in base64, but you can supply your own encoding function
const paginatedResult = getPaginatedResult<User>(paginationParams, limit, result);
// Output:
// {
// data: T[],
// meta: {
// limit: number,
// hasMoreData: boolean,
// cursor: string,
// backCursor: string,
// count: number
// }
// }
Security disclaimer
It's important to validate that the cursor has been generated by your service before passing it to the DynamoDB. If you don't, this opens a NoSQL vulnerability. A solution for this is signing/encrypting the cursor with a key.
Without encrypting the cursor, the partition and range key are also visible to the client consuming the cursor.
If your service offers authentication, it's also wise to validate that the cursor being parsed, was originally generated for that user/session. This is to prevent replay attacks.
Cursor encryption example
A simplified example of encrypting and decrypting the generated pagination cursor.
It's recommended to encapsulate the secured pagination code in a service, for ease of use.
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto';
import { getPaginatedResult, decodeCursor } from 'dynamodb-paginator';
const ENC_KEY = randomBytes(32); // set random encryption key
const IV = randomBytes(16); // set random initialisation vector
const ALGORITHM = 'aes-256-cbc';
const encrypt = ((val) => {
const cipher = createCipheriv(ALGORITHM, ENC_KEY, IV);
let encrypted = cipher.update(JSON.stringify(val), 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
});
const decrypt = ((encrypted) => {
const decipher = createDecipheriv(ALGORITHM, ENC_KEY, IV);
const decrypted = decipher.update(encrypted, 'base64', 'utf8');
return JSON.parse((decrypted + decipher.final('utf8')));
});
const limit = 25;
const params = { TableName: 'Users', Limit: limit };
// Example DynamoDB Output
const result = {
Items:
[
{ id: 1, email: '[email protected]' },
{ id: 2, email: '[email protected]' },
],
Count: 2,
LastEvaluatedKey: { id: 2 },
};
// Pass a custom encoding function
const paginatedResult = getPaginatedResult(params, limit, result, encrypt);
// Pass a custom decoding function
const decodedCursor = decodeCursor(paginatedResult.meta.cursor, decrypt);
console.log(decodedCursor);
// Output:
// {
// TableName: 'Users',
// Limit: 25,
// ExclusiveStartKey: {id:2},
// previousKeys: [{id:2}],
// back: false
// }
API Reference
Functions
- getPaginatedResult(params, limit, result, cursorEncodingFunction) ⇒
PaginatedResult<T>
- decodeCursor(encodedCursor, cursorDecodingFunction) ⇒
DynamoDBParams
|undefined
Typedefs
- DynamoDBParams :
Object
- DynamoDBResult :
Object
- MetaData :
Object
- PaginatedResult :
Object
PaginatedResult<T>
getPaginatedResult(params, limit, result) ⇒ Kind: function
Param | Type |
---|---|
params | DynamoDBParams |
limit | number |
result | DynamoDBResult |
cursorEncodingFunction? | (cursor: DynamoDBParams) => string |
Cursor
| undefined
decodeCursor(cursor) ⇒ Kind: function
Param | Type |
---|---|
encodedCursor | string |
cursorDecodingFunction? | (encodedCursor: string) => DynamoDBParams |
Object
DynamoDBParams : Kind: object Properties
Name | Type | Description |
---|---|---|
TableName | string |
The name of the table containing the requested items |
[IndexName] | string |
The name of a secondary index to scan |
[AttributesToGet] | any |
This is a legacy parameter. Use ProjectionExpression instead. |
[Limit] | number |
The maximum number of items to evaluate |
[Select] | any |
The attributes to be returned in the result |
[ScanFilter] | any |
This is a legacy parameter |
[ConditionalOperator] | any |
This is a legacy parameter |
[ExclusiveStartKey] | any |
The primary key of the first item that this operation will evaluate |
[ReturnConsumedCapacity] | any |
Adds the consumed capacity to the result |
[TotalSegments] | any |
For a parallel Scan request |
[Segment] | any |
For a parallel Scan request |
[ProjectionExpression] | string |
A string that identifies one or more attributes to retrieve from the specified table or index |
[FilterExpression] | string |
A string that contains conditions that DynamoDB applies after the Scan operation |
[ExpressionAttributeNames] | any |
One or more substitution tokens for attribute names in an expression |
[ExpressionAttributeValues] | any |
One or more values that can be substituted in an expression |
[ConsistentRead] | boolean |
A Boolean value that determines the read consistency model during the scan |
Object
DynamoDBResult : Kind: object Properties
Name | Type | Description |
---|---|---|
[Items] | any |
An array of item attributes that match the scan criteria |
[Count] | number |
The number of items in the response |
[ScannedCount] | number |
The number of items evaluated |
[LastEvaluatedKey] | any |
The primary key of the item where the operation stopped |
[ConsumedCapacity] | any |
The capacity units consumed by the Scan operation |
Object
MetaData : Kind: object Properties
Name | Type | Description |
---|---|---|
limit | number |
The limit of the amount of returned items |
hasMoreData | boolean |
True if not all items in the DynamoDB table were returned that match the query |
cursor | string |
Used for pagination if there are more items left |
backCursor? | string |
Used for paginating back to previous results |
count | number |
The amount of items returned |
Object
PaginatedResult : Kind: object Properties
Name | Type | Description |
---|---|---|
data | T |
The queried data |
meta | MetaData |
Metadata regarding the result |