[Lab] DymamoDB Pagination
If you send a GET
request using this URL: https://{{apiId}}.execute-api.us-east-1.amazonaws.com/dev/groups?limit=2
you should get a reply like this:
{
"items": [
...
],
"nextKey": "%7B%22id%22%3A%221%22%7D"
}
Notice that you need to replace {{apiId}}
with an id of the API that you've deployed.
If you pass the nextKey
and use it with another GET
request using this URL: https://{{apiId}}.execute-api.us-east-1.amazonaws.com/dev/groups?limit=2&nextKey=%7B%22id%22%3A%221%22%7D
{
"items": [
...
],
"nextKey": null
}
If nextKey
is null
it means that there are no more items to return.
Notice that the value of the LastEvaluatedKey
in a DynamoDB result is a JSON object. To pass it in another GET request we convert it to a string and then use URI encoding to allow to pass it in a URL:
encodeURIComponent(JSON.stringify(lastEvaluatedKey))
To pass a value coming in a GET request you would need to first decode a string and then parse a JSON string:
// Value read from a query parameter
const nextKeyStr = ...
// "exclusiveStartKey" can be passed a parameter to a "scan()" call
const exclusiveStartKey = JSON.parse(decodeURIComponent(nextKeyStr))
'use strict' const AWS = require('aws-sdk') const docClient = new AWS.DynamoDB.DocumentClient() const groupsTable = process.env.GROUPS_TABLE exports.handler = async (event) => { console.log('Processing event: ', event) let nextKey // Next key to continue scan operation if necessary let limit // Maximum number of elements to return try { // Parse query parameters nextKey = parseNextKeyParameter(event) limit = parseLimitParameter(event) || 20 } catch (e) { console.log('Failed to parse query parameters: ', e.message) return { statusCode: 400, headers: { 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify({ error: 'Invalid parameters' }) } } // Scan operation parameters const scanParams = { TableName: groupsTable, Limit: limit, ExclusiveStartKey: nextKey } console.log('Scan params: ', scanParams) const result = await docClient.scan(scanParams).promise() const items = result.Items console.log('Result: ', result) // Return result return { statusCode: 200, headers: { 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify({ items, // Encode the JSON object so a client can return it in a URL as is nextKey: encodeNextKey(result.LastEvaluatedKey) }) } } /** * Get value of the limit parameter. * * @param {Object} event HTTP event passed to a Lambda function * * @returns {number} parsed "limit" parameter */ function parseLimitParameter(event) { const limitStr = getQueryParameter(event, 'limit') if (!limitStr) { return undefined } const limit = parseInt(limitStr, 10) if (limit <= 0) { throw new Error('Limit should be positive') } return limit } /** * Get value of the limit parameter. * * @param {Object} event HTTP event passed to a Lambda function * * @returns {Object} parsed "nextKey" parameter */ function parseNextKeyParameter(event) { const nextKeyStr = getQueryParameter(event, 'nextKey') if (!nextKeyStr) { return undefined } const uriDecoded = decodeURIComponent(nextKeyStr) return JSON.parse(uriDecoded) } /** * Get a query parameter or return "undefined" * * @param {Object} event HTTP event passed to a Lambda function * @param {string} name a name of a query parameter to return * * @returns {string} a value of a query parameter value or "undefined" if a parameter is not defined */ function getQueryParameter(event, name) { const queryParams = event.queryStringParameters if (!queryParams) { return undefined } return queryParams[name] } /** * Encode last evaluated key using * * @param {Object} lastEvaluatedKey a JS object that represents last evaluated key * * @return {string} URI encoded last evaluated key */ function encodeNextKey(lastEvaluatedKey) { if (!lastEvaluatedKey) { return null } return encodeURIComponent(JSON.stringify(lastEvaluatedKey)) }