Package Exports
- dynamodb-data-types
- dynamodb-data-types/lib/dynamodb-data-types.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (dynamodb-data-types) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
DynamoDb-Data-Types
dynamodb-data-types is a utility to help represent data types and records
used by the AWS SDK.
As of Version 4.0.0 (currently in Beta), this library helps create
UpdateExpression for DynamoDB UpdateItem operations. See
updateExpr() below.
Introduction
Given the below JavaScript data:
const data = {
fruit: 'Apple',
count: 12
}This library converts it to a structure required by DynamoDB:
{
"fruit": { "S": "Apple" },
"count": { "N": "12" }
}Getting Started
Below are some quick examples which do not commnicate with a DynamoDB instance. For full examples, see examples.
wrap, unwrap - to convert (marshall) JavaScript objects.
const attr = require('dynamodb-data-types').AttributeValue;
const data = {
id: 10,
food: ['Rice', 'Noodles'],
age: 1,
isThatYou: true,
stuff: ['Tomato', 33],
day: 'Tuesday'
};
attr.wrap(data); // wrap (marshall) data to use with DynamoDB
/* Returns:
* {
* "id": {"N": "10"},
* "food": {"SS": ["Rice", "Noodles"] },
* "age": {"N": "1"},
* "isThatYou": {"BOOL": true},
* "stuff": {"L": [{"S": "Tomato"}, {"N": "33"}]},
* "day": {"S": "Tuesday"}
* } */
attr.unwrap(dynamodbData); // unwrap (unmarshall) data
/* Returns:
* {
* "id": 10,
* "food": ["Rice", "Noodles"],
* "age": 1,
* "isThatYou": true,
* "stuff": ["Tomato", 33],
* "day": "Tuesday"
* } */To wrap/unwrap individual values, use wrap1 and unwrap1:
console.log(attr.wrap1(50));
//{ N: '50' }
console.log(attr.unwrap1({"N":"50"}));
//50Note: Being a utility, this library only helps build structures required by DynamoDB.
updateExpr() - for DynamoDB UpdateExpression
To update a record, DynamoDB UpdateExpression supports four clauses SET,
REMOVE, ADD, DELETE, each of which accepts one ore more action. See AWS
documentation
for more.
dynamodb-data-types updateExpr() generates DynamoDB UpdateExpression. It
avoids conflict with keywords reserved by
DynamoDB. To
demonstrate this, the below example uses the conflicting keyword year.
const { wrap } = require("dynamodb-data-types").AttributeValue;
const { updateExpr } = require("dynamodb-data-types");
const {
DynamoDBClient,
UpdateItemCommand,
PutItemCommand,
} = require("@aws-sdk/client-dynamodb");
const TableName = "FooTable";
const client = new DynamoDBClient({ region: "us-east-1" });
const updates = updateExpr() // Note: updateExpr() should be called
.set({ greet: "Hello" }) // Chain clauses multiple times
.remove("foo", "city")
.add({ age: 1 })
.set({ nick: "bar" })
.remove("baz")
.delete({ year: [2008] })
.add({ amt: 1.5 });
// updates.expr() gets the data structures.
const {
UpdateExpression,
ExpressionAttributeValues,
ExpressionAttributeNames,
} = updates.expr();
/* Following are some of data structures generated:
* {
* UpdateExpression:
* 'SET greet = :a, nick = :b REMOVE foo, baz ADD age :c, amt :d DELETE #A :e',
*
* ExpressionAttributeValues: {
* ':a': { S: 'Hello' },
* ':b': { S: 'bar' },
* ':c': { N: '1' },
* ':d': { N: '1.5' },
* ':e': { NS: [Array] }
* },
*
* ExpressionAttributeNames: { '#A': 'year' }
* }
*/
const params = {
TableName,
Key: wrap({ id: 10 }),
UpdateExpression,
ExpressionAttributeValues,
ExpressionAttributeNames,
};
/* TIP: For shorter code, use ...updates.expr()
* const params = {
* TableName,
* Key: wrap({ id: 10 }),
* ...updates.expr()
* };
*/
client.send(new UpdateItemCommand(params));updateExpr() avoids creating duplicate values
As demonstrated below, updateExpr() avoids creating duplicate
ExpressionAttributeValues if the value is the same.
It does this by doing a strict equality === check on the value.
/* Below, values are different for all actions/clauses.
* Hence there are three entries in ExpressionAttributeValues.
*/
const expr0 = updateExpr()
.set({ w: 1 })
.set({ x: 2 })
.add({ y: 3 })
.expr();
// {
// UpdateExpression: 'SET w = :a, x = :b ADD y :c',
// ExpressionAttributeValues: {
// ':a': { N: '1' },
// ':b': { N: '2' },
// ':c': { N: '3' },
// }
// }
/* Below, value is the same for all actions/clauses.
* Hence there is one entry in ExpressionAttributeValues.
*/
const expr1 = updateExpr()
.set({ w: 1 })
.set({ x: 1 })
.add({ y: 1 })
.expr();
// {
// UpdateExpression: 'SET w = :a, x = :a ADD y :a',
// ExpressionAttributeValues: {
// ':a': { N: '1' }
// }
// }Roadmap TODO
To avoid duplicate values in ExpressionAttributeValues, apart from doing a strict equality check using '===', allow a deep equality to avoid duplicates.
Below, value is the same array for all actions/clauses. Hence there should be 1 entry in ExpressionAttributeValues.
However there are 3 entries.
It might be a good feature to do a deep equality and ensure 1 entry in ExpressionAttributeValues.
const expr0 = updateExpr()
.set({ w: [1, 2, 3] })
.set({ x: [1, 2, 3] })
.set({ y: [1, 2, 3] })
.expr();
// {
// UpdateExpression: 'SET w = :a, x = :b, y = :c',
// ExpressionAttributeValues: {
// ':a': { NS: ['1', '2', '3'] },
// ':b': { NS: ['1', '2', '3'] },
// ':c': { NS: ['1', '2', '3'] },
// }
// }See
examples/01-put-and-update-expression.js
for full example of generated DynamoDB structures UpdateExpression,
ExpressionAttributeValues, ExpressionAttributeNames.
attrUpdate - for DynamoDB AttributeUpdates (Deprecated)
dynamodb-data-types attrUpdate() generates
AttributeUpdates
which is a
legacy
parameter. DynamoDB AttributeUpdates is deprecated in favor of
UpdateExpression described above.
const attrUpdate = require('dynamodb-data-types').AttributeValueUpdate;
const dataUpdates = attrUpdate
.put({game: "Football"})
.add({age: 1})
.delete("day");
// {
// "game": {
// "Action": "PUT",
// "Value": {"S": "Football"}
// },
// "age": {
// "Action": "ADD",
// "Value": {"N": "1"}
// },
// "day": {
// "Action": "DELETE"
// }
// }Use with Node.js
Use with AWS SDK for Node.js
npm install dynamodb-data-typesUse in cli
Use with the cli for quick utility
npm install -g dynamodb-data-types
dynamo-dt-attr-wrap '{"hello":"world"}'
dynamo-dt-attr-unwrap '{"hello": {"S": "world"}}'Use in the browser
Use with AWS SDK for JS in the Browser
Download the browser version from dist.
See examples/browser and this note
Notes for use in the browser
The browser version of this library (created using browserify) has not been tested. Pull requests to add tests for the browser are welcome (maybe using phantom.js?).
The browser version is available from version 2.1.2 onwards.
File size of the browser version
The browser version of this library is generated using Browserify.
For versions 3.0.0 onwards of this library, browserify is made to exclude
Buffer related code. It is less likely for a browser side application to make
use of Buffer as a binary type.
If you don't need detailed info about this, skip the next paragraph.
This library uses node's Buffer for
recognizing binary types. By default, browserify, includes external Buffer
related code, causing the filesize of the browser dist to become 5.4
times larger (6x if you compare min.js files). Version 3.0.0 onwards,
browserify is made to exclude Buffer related code because it seems less
likely for browser side code to detect Buffer as a binary type. Incase your
browser application does require Buffer you might try using
dist-with-buffer
Examples
- examples/01-put-and-update-expression.js
- examples/02-binary-image.js
- examples/03-explicit-data-type.js
- examples/04-explicit-preserve-arrays.js
- examples/browser/dynamodb-data-types.html
- examples/depricated-01-put-update.js
Features
Refer to docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Types.html
DynamoDb-Data-Types supports:
- AttributeValue
- AttributeValueUpdate
Supported AttributeValue types
Refer to docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html
DynamoDb-Data-Types supports:
- B
- BOOL
- BS
- L
- M
- N
- NS
- NULL
- S
- SS
preserveArrays
(New in version 2.1.0)
Consider the following:
const data = {
alphabets: ['c', 'a', 'b', 'c']
};
wrap(data) detects alphabets as SS. Being a set SS has two properties unlike those of arrays :
- The order of elements is not preserved.
- Duplicate elements are not allowed.
Starting with version 2.1.0, you can do:
wrap(data, {types: {alphabets: 'L'} }to explicitly tell wrap to treat itLinstead of the auto-detectedSS. Similarly forput()andadd()- Alternatively, call
preserveArrays()to consider all arrays as typeL. This has a global effect.
Read the documentation and examples for more.
Support for BOOL, NULL, M, L
(new in version 2.0.0)
DynamoDb-Data-Types version 2.0.0 introduces support for AttributeValue
types BOOL, NULL, M, L.
Use of M for nested data
DynamoDb-Data-Types uses M to nest objects. Consider the following data:
const data = {
polygon: {
quadrilateral: {
sides: 4
}
}
}wrap() maps the above data as:
{
"polygon": {
"M": {
"quadrilateral": {
"M": {
"sides": {
"N": "4"
}
}
}
}
}
}Use of L for arrays
DynamoDb-Data-Types uses L to represent mixed arrays. Consider the following data:
{
strs: ['abc', 'def'],
nums: [123, 456],
mix: [1, 'abc', true, false, null, [1,2,3]]
}wrap() maps the above data as:
{
strs: {
SS: ["abc","def"]
},
nums: {
NS: ["123","456"]
},
mix: {
"L": [
{ N: "1" },
{ S: "abc" },
{ BOOL: true },
{ BOOL: false },
{ NULL: true },
{ NS: ["1","2","3"] }
]
}
}Detecting data types
It is straightforward to detect types N, NS, S, SS, NULL and BOOL.
To detect other types - M, L, B, BS - simple rules are applied as
explained below.
For any a given value val, wrap() detects the AWS Data types as follows:
BOOL, NULL, N, S
How wrap() detects them (psuedo-code):
IF val is typeof boolean
THEN detect as type BOOL
ELSE IF val is null
THEN detect as type NULL
ELSE IF val is typeof number or if val instanceof Number
THEN detect as type N
ELSE IF val is typeof string or if val is instanceof String
THEN detect as type SB
How wrap() detects type B (psuedo-code):
IF val is instanceof Buffer
THEN detect as type BThere maybe other types which should get detected as B. Please let me know if
you have suggestions.
M
How wrap() detects type M (psuedo-code):
IF (val is none of: BOOL, NULL, N, S, B)
AND (typeof val === 'object')
THEN detect as type M
ELSE
wrap() ignores valNS, SS, BS, L
When wrap() sees an Array, here's what it does (psuedo-code):
IF val is an Array
IF (every element in Array is type N)
THEN detect as type NS
ELSE IF (every element in Array is type S)
THEN detect as type SS
ELSE IF (every element in Array is type B)
THEN detect as type BS
ELSE
detect as type LAPI - Reference documentation
Global settings
preserveArrays()
If preserveArrays() is called, all arrays found in the object being wrapped
are given type L. In other words, arrays will no longer get detected as NS,
SS or BS but specified as L.
This is useful to preserve duplicates and the order of elements in arrays.
const ddt = require('dynamodb-data-types');
ddt.preserveArrays();This function is designed to be called once - It has a global effect.
If this is not needed on a global level, a similar effect can be achieved using
options parameter passed to wrap(), wrap1() and put() and add().
Similarly, the global behaviour of preserveArrays() may be overridden using
the options object passed to wrap(), wrap1() and put() and add().
AttributeValue
AWS API Reference - AttributeValue
AttributeValueUpdate
AWS API Reference - AttributeValueUpdate
AttributeValue
wrap(item[, options])
Wrap (marshall) JavaScript data into DynamoDB's AttributeValue data type.
Arguments
- @param {Object} item The object to wrap.
- @param {Object} options
- @return {Object} A DynamoDB AttributeValue.
Options
types: An object containing attribute names and explicit type for that attribute. Currently explicit type can only be specified if the detected type is an array. Possible values are'NS','SS','BS','L'
Example of an options object:
// Any property named 'randomList' found in the object (at any depth) is
// specified as 'NS'. This explicit type can be assigned only if `randomList` is
// detected as an array.
// Similarly if 'orderedList' is an array, it gets specified as type 'L'
{
types: {
randomList: 'NS',
orderedList: 'L'
}
}Example
const attr = require('dynamodb-data-types').AttributeValue;
attr.wrap({name: "Foo", age: 50});
// {"name":{"S":"Foo"},"age":{"N":"50"}}
attr.wrap({alphabets: ["a", "b", "c"]});
// {"alphabets":{"SS": ["a","b","c"]}}
attr.wrap({alphabets: ["a", "b", "c"]}, {types: {alphabets:"L"}});
// {"alphabets":{"L": [{"S":"a"},{"S":"b"},{"S": "c"}]}}unwrap(attributeValue)
Unwrap (unmarshall) DynamoDB AttributeValue to appropriate JavaScript types.
Arguments
- @param {Object} attributeValue The DynamoDB AttributeValue to unwrap.
- @return {Object} Unwrapped object with properties.
Example
const attr = require('dynamodb-data-types').AttributeValue;
attr.unwrap({"name":{"S":"Foo"},"age":{"N":"50"}});
// {name: "Foo", age: 50}wrap1(value [, options])
Wrap a single value into DynamoDB's AttributeValue.
Arguments
- @param {String|Number|Array}
- @param {Object} options Same as options for wrap().
- @return {Object} DynamoDB AttributeValue.
Example
const attr = require('dynamodb-data-types').AttributeValue;
attr.wrap1(50); // {"N":"50"}
attr.wrap1("50"); // {"S":"50"}unwrap1(attributeValue)
Unwrap a single DynamoDB's AttributeValue to a value of the appropriate JavaScript type.
Arguments
@param {Object} attributeValue The DynamoDB AttributeValue. @return {String|Number|Array} The JavaScript value.
Example
const attr = require('dynamodb-data-types').AttributeValue;
attr.unwrap1({"N":"50"}); // 50
attr.unwrap1({"S":"50"}); // "50"
AttributeValueUpdate
add(attrs [, options])
Append attributes to be updated with action "ADD".
This function can be chained with further calls to add, put or delete.
Arguments
- @param {Object} attrs Object with attributes to be updated.
- @param {Object} options Same as options for wrap().
- @return {Updates} Object with all update attributes in the chain.
See note: duplicate attribute names
put(attrs [, options])
Append attributes to be updated with action "PUT".
This function can be chained with further calls to add, put or delete.
Arguments
- @param {Object} attrs Object with attributes to be updated.
- @param {Object} options Same as options for wrap().
- @return {Updates} Object with all update attributes in the chain.
See note: duplicate attribute names
delete(attrs)
Append attributes to be updated with action "DELETE".
This function can be chained with further calls to add, put or delete.
Arguments
@param {Object|String|Array} attrs If this argument is an an Object,the Object's property values must be an array, containing elements to be removed, as required by DynamoDB SDK. If this argument is a String, it should contain comma seperated names of properties to be deleted. If its an Array, each array element should be a property name to be deleted.
@return {Updates} Object with all update attributes in the chain.
See note: duplicate attribute names
Example: put, add, delete
const attrUpdate = require('dynamodb-data-types').AttributeValueUpdate;
const dataUpdate = attrUpdate
.put({name: "foo"})
.add({age: 1})
.delete("height, nickname")
.add({favColors: ["red"]})
.delete({favNumbers: [3]});
console.log(JSON.stringify(dataUpdate));
// {
// "name": { "Action": "PUT", "Value": { "S": "foo" } },
// "age": { "Action": "ADD", "Value": { "N": "1" } },
// "height": { "Action": "DELETE" },
// "nickname": { "Action": "DELETE" },
// "favColors": { "Action": "ADD", "Value": { "SS": ["red" ] } },
// "favNumbers": { "Action": "DELETE", "Value": { "NS": ["3"] } }
// }Note: Duplicate attribute names in AttributeValueUpdate
Each attribute name can appear only once in the AttributeUpdates object of the
itemUpdate call. This is a feature of the AWS API. However its easy to
overlook this when chaining add, put and delete updates.
For example, following is an attribute colors of type SS (String set)
const item = {
id: ...,
colors: ["red", "blue"]
}Suppose, we want to delete "red" and add "orange".
To add "orange", the AttributeUpdates object is created as:
attrUpdate.add({colors: ["orange"]}). Similarly, to delete "red" the
AttributeUpdates object is created as attrUpdate.delete({colors: ["red"]})
However, both actions cannot be represented in the same AttributeUpdates
object.
// Will not work as expected
attrUpdate.add({colors: ["orange"]}).delete({colors: ["red"]});The action to delete "red" overwrites the action to add "orange". This is
simply because colors is a property of the AttrubuteUpdates object.
The following code demonstrates the above note:
JSON.stringify(attrUpdate.add({colors: ["orange"]}));
//{"colors":{"Action":"ADD","Value":{"SS":["orange"]}}}
JSON.stringify(attrUpdate.delete({colors: ["red"]}));
//{"colors":{"Action":"DELETE","Value":{"SS":["red"]}}}
// The below does not work as expected
JSON.stringify(attrUpdate.add({colors: ["orange"]}).delete({colors: ["red"]}));
//{"colors":{"Action":"DELETE","Value":{"SS":["red"]}}}
Older versions of DynamoDb-Data-Types
Read this only if you need DynamoDb-Data-Types version 1.0.0 or below.
If you are already using version 1.0.0 or 0.2.7 you may continue to do so.
If you are using DynamoDb-Data-Types version 1.0.0 or 0.2.7, wrapping / unwrapping B and BS will not work when used with AWS SDK 1.x.x
but should automagically work with AWS SDK 2.x.x. although it has not been
tested. This is related to automatic conversion of base64 done by AWS SDK
version 2.x. See
AWS Upgrading Notes (1.x to 2.0).
Change log
Version 4.0.0
- Introduce support for DynamoDB
UpdateExpressionwhich also usesExpressionAttributeValuesandExpressionAttributeNames.
Version 3.0.3
- Update code examples and docs
Functionally, this version is identical to the previous 3.0.2
Version 3.0.2
- Update the
lodashversion (used for tests).
Functionally, this version is identical to the previous 3.0.1
Version 3.0.1
- Expose as a CLI utility thanks to @bneigher (github.com/bneigher).
Functionally, apart from the CLI utility, this version is identical to the previous 3.0.0
Version 3.0.0
- For Node users, version
3.0.0is identical to2.1.6 - For browser side version of this library
- In version
3.0.0onwardsBufferrelated code has been excluded. - Filesize of the
min.jsversion is now6.5KB. Earlier it was40KB.
- In version
Version 2.1.2 - 2.1.6
- Added/fixed tests to imporve coverage.
- Reviewed docs.
Source code of versions 2.1.2 to 2.1.6 are identical to 2.1.1.
Version 2.1.2
This version is identical to 2.1.1 with no changes to code. It only includes a JS build for the browser plus a few more tests.
- Use browserify to create a dist for use in the browser.
- Updated tests, use travis-ci, coverage, istanbul, .jshintrc.
Version 2.1.1
2015-12-18
- Replace functions deprecated by Node.
Version 2.1.0
2015-08-17
- Call
preserveArrays()to use typeLfor array types; this preserves order of array elements and allows duplicate array elements both of which are not possible using setsSS,NSorBS - If not required on a global scale (calling preserveArrays), explicity set array types by passing opts to
wrap(),add(),put()
Version 2.0.1
2015-02-15
- Fixed README
- Committed modified package.json (just realised it wasn't committed)
Version 2.0.0
2015-02-15
- Implemnted
M - Implemented
L - Added example to put and get binary data (examples/02-binary-image.js)
Version 1.0.0
2015-02-11
Note: There are no source code changes in version 1.0.0. Functionally, 1.0.0 is identical to 0.2.7.
- Bumped from version 0.2.7 to version 1.0.0.
- Update documentation especially with regard to
BandBSdata types. - Added development deps into pacakge.json instead of tests/package.json (It should have been this way to begin with)
version 0.2.7
2014-01-29
version 0.2.6
2013-11-15
version 0.2.5
2013-11-11
Note: Change log dates are yyyy-mm-dd.