3.1.0 • Published 5 days ago

dynamodb-paginator v3.1.0

Weekly downloads
16
License
MIT
Repository
github
Last release
5 days ago

npm NPM CircleCI codecov

NOTE: This pagination library only works on indexes with a sort key.

Usage

Compatible with AWS SDK v3

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { QueryCommand, DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
import { getPaginatedResult, decodeCursor } from "dynamodb-paginator";

interface UserPet {
  userId: number; // partition key (hash)
  petId: number; // sort key (range)
}

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

const limit = 25;
const defaultInput = {
  TableName: "UserPets",
  Limit: limit,
  KeyConditionExpression: "userId = :userId",
  ExpressionAttributeValues: {
    ":userId": 1,
  },
  ConsistentRead: true,
};

// Could be a cursor from a previous paginated result
const cursor = undefined;
const paginationInput = decodeCursor(cursor) || defaultInput;

const command = new QueryCommand(paginationInput);

const response = await docClient.send(command);

// By default the cursors are encoded in base64, but you can supply your own encoding function
const paginatedResult = getPaginatedResult<UserPet>(
  paginationInput,
  limit,
  response
);

// 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 = 2;
const params = { TableName: "UserPets", Limit: limit };
// Example DynamoDB Output
const result = {
  Count: 2,
  Items: [
    { userId: 1, petId: 1 },
    { userId: 1, petId: 2 },
  ],
  LastEvaluatedKey: { userId: 1, petId: 2 },
  ScannedCount: 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: 'UserPets',
//   Limit: 2,
//   ExclusiveStartKey: { userId: 1, petId: 2 },
//   previousKeys: [ { userId: 1, petId: 2 } ],
//   back: false
// }
3.1.0

5 days ago

3.0.5

3 months ago

3.0.4

4 months ago

3.0.3

5 months ago

3.0.2

5 months ago

3.0.1

5 months ago

3.0.0

5 months ago

2.2.0

1 year ago

2.0.3

3 years ago

2.0.2

3 years ago

2.1.0

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.0.9

3 years ago

1.0.8

3 years ago

1.0.7

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago

0.0.10

4 years ago

0.0.9

4 years ago

0.0.8

5 years ago

0.0.7

5 years ago

0.0.6

5 years ago

0.0.5

5 years ago

0.0.4

5 years ago

0.0.3

5 years ago

0.0.2

5 years ago

0.0.1

5 years ago