DynamoDB NodeJS SDK By Examples

Document Client

Use the Document Client whenever you can!

It is mostly the same as the original low-level API and accepts same parameters, but adding the convenience of doing conversion (marshall() / unmarshall()) between DynamoDB and JavaScript data types.

const AWS = require('aws-sdk');

const doc = new AWS.DynamoDB.DocumentClient(options)

Callback

By default, the second parameter of all APIs is a callback in following form.

doc.get(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

Failing to provide the callback will stop auto-sending the API request. User will need to manually add .send() after the method to send a request without callback.

For simplicity, following code examples DO NOT show the callback parameter. User should add callback or use async calls (see below).

Common Parameters

These are common parameters shared by most of the operations, add them to the parameter object as needed.

Condition Expression

{
  ConditionExpression: 'a = 1 AND b <> "test"'
}

Condition expression functions:

  • attribute_exists(attr)
  • attribute_not_exists(attr)

Projection Attribute Names and Values

DynamoDB has a lot of reserved key words. They clash with user-defined attribute names often. So it is best to map attribute names to something else, e.g. prefix them with a hash (#).

Also you can count on DynamoDB engine to convert data into their string representations to avoid injection. The convention is to prefix a value mapping name with a comma (:).

You may use the mapped names in any parameter where an attribute name or value is needed.

{
  ExpressionAttributeNames: [
    '#someAttr': 'someAttr'
  ],
  ExpressionAttributeValues: [
    ':someValue': 'injections in this value is not possible'
  ]
}

Access an Item by Its Key

doc.get({
  Key: {
    id: 'example',
    rangeKey: 'example'
  },
  AttributesToGet: [
    'someAttr'
  ]
  TableName: 'MyTable'
})

Create a New Item / Replace an Item with Specified Key

You add new item with put(). You also replace items with same primary key with it.

To prevent overwrite, add attribute_not_exists() condition expression on its primary key(s).

doc.put({
  Item: myItem,
  ConditionExpression: 'attribute_not_exists(#id)',
  ExpressionAttributeNames: {
    '#id': 'id'
  },
  TableName: myTable
});
  • (a) attribute_not_exists checks whether an item with same primary key as the to-be-inserted item exists
  • (b) Additionally, it checks whether an attribute exists on that item, value does not matter
  • If you only want to prevent overwriting, then use attribute_not_exists with primary key (or partition key, or range key), since the key must exist, check (b) will always pass, only check (a) will be in effect

Update an Item with Specified Key

This is different from put() as it only updates given attributes and leave others, whereas put() will replace the whole item with given item.

You can see this very clearly given the different parameters they require.

doc.update({
  Key: myKey,
  UpdateExpression: 'SET #a = :a',
  ExpressionAttributeNames: {
    '#a': 'a'
  }),
  ExpressionAttributeValues: {
    ':a': 'value'
  },
  ReturnValues: 'UPDATED_NEW',
  TableName: myTable
});

Delete an Item by Its Key

Deleting an item is straightforward.

Two things to note:

  • It does not throw errors if given key does not exist (to be idempotent)
  • It only accepts ALL_OLD or NONE for ReturnValues
doc.delete({
  Key: myKey,
  TableName: myTable
});

Async Call

Append any API call with .promise() to make an asynchronous call to the API and returns a Promise object. There are two ways to use the promise.

### METHOD 1 ###

doc.put({
  Item: myItem,
  ConditionExpression: 'attribute_not_exists(#id)',
  ExpressionAttributeNames: {
    '#id': 'id'
  },
  TableName: 'myTable'
})
.promise()
.then((result) => {
  // deal with results
})
.catch((e) => {
  // check e.code
});

### METHOD 2 ###

try {
  const result = await doc.put({
    Item: myItem,
    ConditionExpression: 'attribute_not_exists(#id)',
    ExpressionAttributeNames: {
      '#id': 'id'
    },
    TableName: 'myTable'
  }).promise();
} catch (e) {
  // check e.code
});

Making a promise will send the request immediately.

DAX

const AmazonDaxClient = require('amazon-dax-client');
var AWS = require("aws-sdk");

const dax = new AmazonDaxClient({endpoints: endpoint, region: region});
const daxClient = new AWS.DynamoDB.DocumentClient({service: dax});

// use document client as usual