dynamodb-wrapper
Version:
A DynamoDB library that extends aws-sdk with bulk read/write, events, streams, and more
313 lines (249 loc) • 12.8 kB
Markdown
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![License][license-image]][license-url]
[npm-image]: https://img.shields.io/npm/v/dynamodb-wrapper.svg
[npm-url]: https://www.npmjs.com/package/dynamodb-wrapper
[travis-image]: https://img.shields.io/travis/Shadowblazen/dynamodb-wrapper.svg
[travis-url]: https://travis-ci.org/Shadowblazen/dynamodb-wrapper
[coveralls-image]: https://img.shields.io/coveralls/Shadowblazen/dynamodb-wrapper.svg
[coveralls-url]: https://coveralls.io/github/Shadowblazen/dynamodb-wrapper?branch=master
[license-image]: https://img.shields.io/npm/l/dynamodb-wrapper.svg
[license-url]: https://opensource.org/licenses/MIT
## What is dynamodb-wrapper?
- **Enhanced AWS SDK:** Public interface closely resembles the AWS SDK, making it easier to learn and use.
- **Bulk I/O:** Easily read, write or delete entire collections of items in DynamoDB with a single API call.
- **Events:** Add event hooks to be notified of important events, such as whenever read/write capacity is consumed, or requests are retried due to throttling.
- **Table prefixes:** DynamoDBWrapper can add a table name prefix in requests and remove it in responses. This is helpful if you have multiple environments within the same AWS Account and region.
## Installing
```
npm install dynamodb-wrapper
```
## Usage
### Setup
Construct the DynamoDBWrapper class
```js
var AWS = require('aws-sdk');
var DynamoDBWrapper = require('dynamodb-wrapper');
var dynamoDB = new AWS.DynamoDB({
// optionally disable AWS retry logic - reasoning explained below
maxRetries: 0
});
// see the Configuration section of the README for more options
var dynamoDBWrapper = new DynamoDBWrapper(dynamoDB, {
// optionally enable DynamoDBWrapper retry logic
maxRetries: 6,
retryDelayOptions: {
base: 100
}
});
```
*(Optional)* If you use DynamoDBWrapper retry logic instead of AWS retry logic, you gain the following benefits:
1. Improved batch processing: DynamoDBWrapper will automatically retry any *UnprocessedItems* in your `batchWriteItem` requests.
2. You can add a `retry` event listener to be notified when requests are throttled. In your application, you can log these events, or even respond by increasing provisioned throughput on the affected table.
3. DynamoDBWrapper's `retryDelayOptions` actually work as documented (this functionality doesn't work in the AWS JavaScript SDK yet, but there's an [open ticket for this feature request](https://github.com/aws/aws-sdk-js/issues/1100)).
```
dynamoDBWrapper.events.on('retry', function (e) {
console.log(
'An API call to DynamoDB.' + e.method + '() acting on table ' +
e.tableName + ' was throttled. Retry attempt #' + e.retryCount +
' will occur after a delay of ' + e.retryDelayMs + 'ms.'
);
});
// An API call to DynamoDB.batchWriteItem() acting on table MyTable
// was throttled. Retry attempt #3 will occur after a delay of 800ms.
```
*(Optional)* If you use the `ReturnConsumedCapacity` property in your AWS requests, the `consumedCapacity` event listener can notify you whenever read/write capacity is consumed.
```
dynamoDBWrapper.events.on('consumedCapacity', function (e) {
console.log(
'An API call to DynamoDB.' + e.method + '() consumed ' +
e.capacityType, JSON.stringify(e.consumedCapacity, null, 2)
);
});
// An API call to DynamoDB.batchWriteItem() consumed WriteCapacityUnits
// [
// {
// "TableName": "MyTable",
// "CapacityUnits": 20
// }
// ]
```
*(Optional)* When using the `DynamoDBWrapper.batchWriteItem()` API method, there is a `batchGroupWritten` event that will notify you of how many items have been processed so far.
```
dynamoDBWrapper.events.on('batchGroupWritten', function (e) {
console.log(e.tableName, e.processedCount);
});
```
### Example: Bulk Read
Read large collections of data from a DynamoDB table with a single API call. Multiple pages of data are aggregated and returned in a single response.
@see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html
```js
// params for Query - same format as in the AWS SDK
var sampleQueryParams = {
TableName: 'MyTable',
KeyConditionExpression: 'MyPartitionKey = :pk',
ExpressionAttributeValues: {
':pk': {
N: '1'
}
}
};
// fetches all pages of data from DynamoDB
// promise resolves with the aggregation of all pages,
// or rejects immediately if an error occurs
dynamoDBWrapper.query(sampleQueryParams)
.then(function (response) {
console.log(response.Items);
})
.catch(function (err) {
console.error(err);
});
```
### Example: Bulk Write/Delete
Insert or delete large collections of items in one or more DynamoDB tables with a single API call. DynamoDBWrapper batches your requests and aggregates the results into a single response. Use configuration values to fine tune throughput consumption for your use case.
@see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
```js
// params for BatchWriteItem - same format as in the AWS SDK
var sampleParams = {
RequestItems: {
MyTable: [
{
PutRequest: {
Item: {
MyPartitionKey: { N: '1' }
}
}
},
{
DeleteRequest: {
Key: {
MyPartitionKey: { N: '2' }
}
}
},
// this array can have thousands of items ...
],
AnotherTable: [
{
PutRequest: {
Item: {
User: { S: 'Batman' }
}
}
},
{
DeleteRequest: {
Key: {
User: { S: 'Superman' }
}
}
},
// this array can have thousands of items ...
]
}
};
// performs all Puts/Deletes in the array
// promise resolves when all items are written successfully,
// or rejects immediately if an error occurs
dynamoDBWrapper.batchWriteItem(sampleParams, {
// use configuration to control and optimize throughput consumption
// write 10 items to MyTable every 500 milliseconds
// this strategy is best if you have known, consistent item sizes
MyTable: {
partitionStrategy: 'EqualItemCount',
targetItemCount: 10,
groupDelayMs: 500
}
// write up to 50 WCU of data to AnotherTable every 1000 milliseconds
// this strategy is best if you have unknown or variable item sizes,
// because it evenly distributes the items across requests so as
// to minimize throughput spikes (which can cause throttling)
AnotherTable: {
partitionStrategy: 'EvenlyDistributedGroupWCU',
targetGroupWCU: 50,
groupDelayMs: 1000
}
})
.then(function (response) {
console.log(response);
})
.catch(function (err) {
console.error(err);
});
```
### Example: Table Prefixes
You may wish to work with duplicate copies of the same set of tables. For example: "dev-MyTable" and "stg-MyTable" if you have dev and stage environments under the same AWS account. DynamoDBWrapper supports this use-case via a configuration-driven `tableNamePrefix` option.
```js
// load this "environment" variable from a config file
// so it will have different values per environment
// we'll just use dev for this example
var environment = 'dev-';
// Configure DynamoDBWrapper with the environment-specific prefix
// Note: we only do this here in one place; there's no need to
// sprinkle the prefix throughout your codebase.
var AWS = require('aws-sdk');
var DynamoDBWrapper = require('dynamodb-wrapper');
var dynamoDB = new AWS.DynamoDB();
var dynamoDBWrapper = new DynamoDBWrapper(dynamoDB, {
tableNamePrefix: environment
});
// Create the table like usual...
dynamoDBWrapper.createTable({
TableName: 'MyTable',
// more params...
});
// The new table will be named "dev-MyTable" instead of "MyTable"
// This works with all the other API methods too.
var promise = dynamoDBWrapper.getItem({
TableName: 'MyTable',
ReturnConsumedCapacity: 'TOTAL',
// more params...
});
// Although the real table name in AWS DynamoDB is "dev-MyTable",
// the prefix will be stripped from the response for transparency:
promise.then(function (response) {
console.log(response);
});
// {
// ConsumedCapacity: {
// TableName: 'MyTable', // <-- no prefix in the response
// CapacityUnits: 1
// },
// Item: { ... }
// }
// In summary: the prefix is prepended to all requests
// and stripped from all responses
```
## Configuration
*This section is copied from the `IDynamoDBWrapperOptions` interface in the `index.d.ts` file.*
The `DynamoDBWrapper` constructor accepts an optional configuration object with the following properties:
- `tableNamePrefix` (string) - A prefix to add to all requests and remove from all responses.
- `groupDelayMs` (number) - The delay (in millseconds) between individual requests made by `query()`, `scan()`, and `batchWriteItem()`. Defaults to 100 ms.
- `maxRetries` (number) - The maximum amount of retries to attempt with a request. Note: this property is identical to the one described in [the AWS documentation](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#constructor-property).
- `retryDelayOptions` (object) - A set of options to configure the retry delay on retryable errors. Note: this property is identical to the one described in [the AWS documentation](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#constructor-property). Currently supported options are:
- `base` (number) - The base number of milliseconds to use in the exponential backoff for operation retries. Defaults to 100 ms.
- `customBackoff` (Function) - A custom function that accepts a retry count and returns the amount of time to delay in milliseconds. The `base` option will be ignored if this option is supplied.
## API
The `DynamoDBWrapper` class supports a Promise-based API with the following methods. These are wrappers around the AWS SDK method of the same name. Please refer to the AWS [API documentation](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations.html) and [JavaScript SDK documentation](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html) for more details:
The following methods are passed straight through to the AWS JavaScript SDK:
- `createTable(params)`
- `updateTable(params)`
- `describeTable(params)`
- `deleteTable(params)`
- `getItem(params)`
- `updateItem(params)`
- `putItem(params)`
- `deleteItem(params)`
- `batchGetItem(params)`
## Enhanced API methods
The following API methods have enhanced behavior to support bulk I/O:
- `query(params, options)` - Fetches all pages of data from a DynamoDB query, making multiple requests and aggregating responses when necessary.
- `options.groupDelayMs` (number) - the delay between individual requests. Overrides the configuration property of the same name in the constructor. Defaults to 100 ms.
- `scan(params, options)` - Fetches all pages of data from a DynamoDB scan, making multiple requests and aggregating responses when necessary.
- `options.groupDelayMs` (number) - the delay between individual requests. Overrides the configuration property of the same name in the constructor. Defaults to 100 ms.
- `batchWriteItem(params, options)` - Writes or deletes large collections of items in multiple DynamoDB tables, batching items and making multiple requests when necessary.
- `options` is a mapping of table names to option hashes. Each option hash may have the following properties:
- `groupDelayMs` (number) - the delay between individual requests. Overrides the configuration property of the same name in the constructor. Defaults to 100 ms.
- `partitionStrategy` (string) - strategy to use when partitioning the write requests array. Possible values: *EqualItemCount* or *EvenlyDistributedGroupWCU*.
- `targetItemCount` (number) - the number of items to put in each group when using the *EqualItemCount* partition strategy.
- `targetGroupWCU` (number) - the size threshold (in WriteCapacityUnits) of each group when using the *EvenlyDistributedGroupWCU* partition strategy.
## Roadmap
- **Streams:** Add method signatures that return Streams (instead of Promises), allowing for better integration ecosystems such as gulp