Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time




1 Read Request Unit: Maximum of 4KB

Read Type Units
Strongly Consistent 1 Unit
Eventually Consistent Read 1/2 Unit
Transactional Read 2 Units


1 Write Request Unit: Maximum of 1KB

Write Type Units
Standard Write Request 1 Unit
Transactional Write 2 Units


To store Timestamp:

new Date().toISOString()

Designing Your Tables

  • Unlike traditional RDBMs, DynamoDB groups sorts by partitions.
    • Each partition returns sorted result.
  • Fields used for partition keys are immutable.
    • You cannot change the values for fields which make up the partition key
    • Bad idea to use name as the partition key, because you won't be able to change the name of an object.



For all things naming regarding DynamoDB.

  • DynamoDB doesn't allow you to rename tables
  • DynamoDB has Reserved Words, so you'll need to use ExpressionAttributeNames to work around them.
  • Table names should be prefixed when deploying. Suggested $service-$stage-$table, for example build-my-prod-events
  • Index names don’t need to be prefixed.
  • Index names can be reused across tables.For example:
    1. Table Events, indexname: updated at
    2. Table organizations, indexname: updated at


DyanmoDB cannot do large "UPDATE queries", for example you cannot do:

UPDATE events
SET status = "past"
WHERE status = "upcoming" AND end_time < NOW();

You have to query each individual event. In this scenario, potentially look at storing the status as a Global Secondary Index. For example:

const response = await dynamodb.queryPromise({
  TableName: process.env.EVENTS_TABLE,
  IndexName: process.env.EVENTS_STATUS_INDEX,
  KeyConditionExpression: "#s = :status AND #e <= :end_time",
    "#s": "status",
    "#e": "end_time", 
  ExpressionAttributeValues: {
    ":status": "upcoming",
    ":end_time": new Date().toISOString(),
  ProjectionExpression: "id",

// We can use either a single BatchWriteItem or multiple UpdateItems
const promises = =>
    TableName: process.env.EVENTS_TABLE,
    Key: {
    UpdateExpression: "SET #s = :status",
      "#s": "status",
    ExpressionAttributeValues: {
      ":status": "upcoming",

await Promise.all(promises);


WARNING: Changing the table name in serverless.yml will drop the table.

DynamoDB Shell

Reserved Words

Work around is to use ExpressionAttributeNames and ExpressionAttributeValues

Example of using the keys:

  TableName: process.env.EVENTS_TABLE,
  IndexName: process.env.EVENTS_STATUS_INDEX,
  KeyConditionExpression: "#s = :status AND #e <= :end_time",
    "#s": "status",
    "#e": "end_time",
  ExpressionAttributeValues: {
    ":status": "upcoming",
    ":end_time": new Date().toISOString(),
  ProjectionExpression: "id",

Examples of Reserved Words:

  • counter
  • name
  • source
  • url
  • uuid
  • value

Promises (async/await)

const { promisify } = require('util');
dynamodb.putPromise = promisify(dynamodb.put);
dynamodb.updatePromise = promisify(dynamodb.update);
dynamodb.getPromise = promisify(dynamodb.get);
result = await dynamodb.putPromise({});
result = await dynamodb.updatePromise({});
result = await dynamodb.getPromise({});


// Does not throw an exception if record does not exist
const result = await dynamodb.getPromise({});

if (!result.Item) {
  // exit early

GetItem cannot be preformed on a Global Secondary Index. You can't use IndexName.


Will overwrite values if the key is the same; unless you define a ConditionalExpression

If an item that has the same primary key as the new item already exists in the specified table, the new item completely replaces the existing item. You can perform a conditional put operation (add a new item if one with the specified primary key doesn't exist), or replace an existing item if it has certain attribute values. You can return the item's attribute values in the same operation, using the ReturnValuesparameter.


    TableName: 'Example',
    Item: {
      CounterName: 'Example1',
    ConditionExpression: 'attribute_not_exists(CounterName)'
  function (error, data) {
    if (error) {
      // If conflict, an error will be thrown
    } else {

When using ConditionExpression, and if there is a conflicting.

Sample error:

    "message":"The conditional request failed",

Query vs Scan

Query Scan
The Query operation finds items based on primary key values. You can query any table or secondary index that has a composite primary key (a partition key and a sort key). The Scan operation returns one or more items and item attributes by accessing every item in a table or a secondary index. To have DynamoDB return fewer items, you can provide a FilterExpression operation.


Sorting is all done on the same partition.

Have to partition wisely.

Global Secondary Index Local Secondary Index
An index with a partition key and a sort key that can be different from those on the base table. An index that has the same partition key as the base table, but a different sort key.
A global secondary index has no size limitations and has its own provisioned throughput settings for read and write activity that are separate from those of the table. As a result, the total size of indexed items for any one partition key value can't exceed 10 GB. Also, a local secondary index shares provisioned throughput settings for read and write activity with the table it is indexing.
Each table in DynamoDB is limited to 20 global secondary indexes (default limit) Each table in DynamoDB is limited to 5 local secondary indexes.

Global Secondary Index

Cannot perform GetItem. Use Query or Scan.

Reducing DynamoDB costs

  • Look for batch fetch
  • Look for batch writes
  • Look for scans
  • Look for large queries
  • Check projections