Introduction

ColdFusion, so far, was limited to storing and retrieving data from relational databases only. In existing versions of ColdFusion, you can store and retrieve data using Hibernate or JDBC drivers. In the 2021 release of ColdFusion, you can store and retrieve data from NoSQL databases, for example DynamoDB.

Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale. It's a fully managed, multi-region, database with built-in security, backup and restore, and in-memory caching for internet-scale applications.  

With DynamoDB, you can create database tables that store and retrieve any amount of data and serve any level of request traffic. You can scale up or scale down your tables' throughput capacity without downtime or performance degradation and use the AWS Management Console to monitor resource utilization and performance metrics.

For more information, see Amazon DynamoDB.

Indexing in DynamoDB

Amazon DynamoDB provides faster access to items in a table by specifying primary key values. However, many applications might benefit from having one or more secondary (or alternate) keys available, to allow efficient access to data with attributes other than the primary key. To address this, you can create one or more secondary indexes on a table, and issue Query requests against these indexes.

A secondary index is a data structure that contains a subset of attributes from a table, along with an alternate key to support Query operations. You can retrieve data from the index using a Query, like the way you issue a query in a table. A table can have multiple secondary indexes, which gives your applications access to many different query patterns.

There are two types of secondary indexes in DynamoDB:

  • Global secondary index — an index with a partition key and a sort key that can be different from those on the base table. A global secondary index is considered "global" because queries on the index can span all the data in the base table, across all partitions.
  • Local secondary index — an index that has the same partition key as the base table, but a different sort key. A local secondary index is "local" in the sense that every partition of a local secondary index is scoped to a base table partition that has the same partition key value.

Supported data types

DynamoDB has a defined set of data types that it supports. The following data types are supported currently:

  • All Numbers – N
  • All Strings – S
  • All Boolean values – BOOL
  • Binary – B
  • Null – NULL
  • Lists – L
  • Map – M
  • Sets – LS, BS, SS

Get started

Install awsdynamodb module

ColdFusion (2021 release) is modularized. By default, the module for AWS DynamoDB is not installed. The first step is to install the DynamoDB module in ColdFusion.

The module for DynamoDB is called awsdynamodb.

To install the module awsdynamodb, follow the steps below:

  1. Navigate to <CF_HOME>/cfusion/bin.

  2. Enter the command:

    • Windows: cfpm.bat
    • Linux: cfpm.sh
  3. Enter the command, install awsdynamodb.

    Wait for the DynamoDB service to get installed.

Get credentials to access DynamoDB

When you interact with AWS, you specify your AWS security credentials to verify your credentials and check whether you have permission to access the resources that you are requesting.

AWS uses the security credentials to authenticate and authorize your requests.

You must get the AWS Access Key ID and the AWS Secret Access Key. For more information, see Access Keys.

Add cloud service credentials

In this release of ColdFusion, there is a new method getCloudService() that gives you a handle to create objects for accessing various cloud services.

The syntax of the service handle is as follows:

service=getCloudService(cloudCred,cloudConfig), where:

  • cloudCred: Defines the credentials for the cloud service. It could either be a struct or a string (also known as credential alias).
  • cloudConfig: Defines the cloud service configuration details. It could either be a struct or a string (also known as config alias).

After you've acquired the AWS credentials, you must declare these credentials in one of the following ways. Only then you can use these credentials to create a DynamoDB  object, after which you can use the object to make calls to the various DynamoDB methods.

ColdFusion Administrator

Set credentials

In the ColdFusion Administrator, click Data & Services > Cloud Credentials

Enter the following details, like credentials Alias, Vendor, and the credentials.

An alias is a named representation of a cloud service and its configuration details. You can set the config alias through ColdFusion Administrator.

Add cloud credentials
Add cloud credentials

After entering the details, click Add Credentials.

Set configuration options

In the ColdFusion Administrator, click Data & Services > Cloud Configuration

Enter the following details, like configuration Alias, Vendor, and the name of the service.

Add cloud configuration options
Add cloud configuration options

After adding the configuration options, you may need to add a few more options. You can do so in the next screen. The following are the option categories that you may need to add:

  • Request config
  • Client config
  • Proxy settings
  • Retry policy
  • Retry conditions

Create the object

Once you've created the aliases for DynamoDB credential and configuration options, you can create the object by using the getCloudService API. For example,

dynamoObject = getCloudService("dynamoCred", "dynamoConf")

Application.cfc

You can specify the DynamoDB credentials and configuration options in Application.cfc. For example,

component {
    this.name="MyApp"
    function OnApplicationStart() {
    application.DynamoProfile = {
               "credentialAlias" : "Alias Name",
               "vendorName" : "AWS",
               "region" : "Region Name",
               "secretAccessKey" : "Access Secret",
               "accessKeyId" : "Access Key"
    }

    application.DynamoCred = {
              "serviceName" = "DYNAMODB"
    }
 }
}

Create the object

dynamoObject = getCloudService(application.DynamoProfile, application.DynamoCred)

On CFM page

On a CFM page, you can specify the DynamoDB credentials and configuration options in one of the four methods, specified below:

Credential alias and configuration alias

After you've created the aliases for DynamoDB credential and configuration options, you can use these aliases in the getCloudService handle as shown below:

<cfscript>
  // define the credential and the configuration aliases in the ColdFusion Admin
  dynamo=getCloudService("dynamoCred","dynamoConf")
  // List all DynamoDB tales in the account
  listTablesStruct = {
    "Limit": 50
  }
  listTablesResponse =dynamo.ListTables(listTablesStruct);
  writeDump(listTablesResponse)
</cfscript>

Credential Alias and Struct for configuration options

<cfscript>
    // Using credential alias and struct for service config
    dynamoConf = {
            "alias":"dynamoConf",
            "serviceName" : "DYNAMODB",
            "clientOverrideConfig":{
                "retryPolicy":{
                  "numRetries":4
                }
            },
            "httpClientConfig":{
                "maxConnections":50
            }

    }
    dynamo = getCloudService("dynamoCred", dynamoConf)

    // List all DynamoDB tales in the account
    listTablesStruct = {
      "Limit": 50
    }
    listTablesResponse =dynamo.ListTables(listTablesStruct);
    writeDump(listTablesResponse)
</cfscript>

Configuration alias and struct for credentials

<cfscript>
    // Using config alias and struct for service credentials
    // dynamo credentials
    dynamoCreds={
      "vendorName":"AWS",
      alias": "dynamoCred",
      "region":"us-east-2",
      "accessKeyId": "access key",
      "secretAccessKey": "secret access"
    }
    dynamo = getCloudService(dynamoCreds, "dynamoConf")
    // List all DynamoDB tales in the account
    listTablesStruct = {
      "Limit": 50
    }
    listTablesResponse =dynamo.ListTables(listTablesStruct);
    writeDump(listTablesResponse)
</cfscript>

Structs for both credential and configuration options

<cfscript>
  // Using Structs for both cloud credential and config
  dynamoCred={
    "vendorName":"AWS",
    "credentialAlias": "dynamoCred",
    "region":"us-east-2",
    "accessKeyId": "access key",
    "secretAccessKey": "secret access key"
  }

  dynamoConf = {
          "alias":"dynamoConf",
          "serviceName" : "DYNAMODB",
          "clientOverrideConfig":{
              "retryPolicy":{
                "numRetries":4
              }
          },
          "httpClientConfig":{
              "maxConnections":50
          }

  }
  dynamo = getCloudService(dynamoCred, dynamoConf)

  // List all DynamoDB tales in the account
  listTablesStruct = {
    "Limit": 50
  }
  listTablesResponse =dynamo.ListTables(listTablesStruct);
  writeDump(listTablesResponse)
</cfscript>

Admin API

You can also add DynamoDB credentials and configuration options by using the Admin APIs. The methods to add credentials and configuration are available in cloud.cfc.

The examples below demonstrate the usage of the methods addCredential(struct credential) and addServiceConfig(struct config).

Add credentials

<cfscript>
  // Create an object of administrator component and call the login method
  adminObj = createObject("component","cfide.adminapi.administrator")
  adminObj.login("admin")

  // Create an object of cloud component
  cloudObj = createObject("component","cfide.adminapi.cloud")

  // define credentials struct
  credentialStruct={
    "alias" : "CredDynamo",
    "vendorName" : "AWS",
    "region" : "us-east-2",
    "secretAccessKey" : "secret access key",
    "accessKeyId" : "access key"
  }

  // add credential credentialStruct
  try{
    cloudObj.addCredential(credentialStruct)
    writeOutput("Credentials added successfully")
  }
  catch(any e){
      writeDump(e)
  }
</cfscript>

Add configuration

<cfscript>
  // Create an object of administrator component and call the login method
  adminObj = createObject("component","cfide.adminapi.administrator")
  adminObj.login("admin")

  // Create an object of cloud component
  cloudObj = createObject("component","cfide.adminapi.cloud")

  // define configuration struct
  configStruct={
      "alias":"ConfDynamo",
      "serviceName":"DYNAMODB",
      "clientOverrideConfig":{
          "retryPolicy":{
              "numRetries":4
          }
      },
      "httpClientConfig":{
          "maxConnections":50
      }
  }

  // add config configStruct
  try{
    cloudObj.addServiceConfig(configStruct)
    writeOutput("Configuration service added successfully")
  }
  catch(any e){
    writeDump(e)
  }
</cfscript>

cfsetup

Cloud Credential

Add

  • add cloudcredential vendorName=AWS accessKeyId=SomeAccessKeyId secretAccessKey=SomeSecretKey region=us-west-1 alias=myAliasForCloudCredential

Set

  • set cloudcredential myAliasForCloudCredential accessKeyId=newAccessKeyId
  • set cloudcredential myAliasForCloudCredential secretAccessKey=newSecretAccessKey
  • set cloudcredential myAliasForCloudCredential region=us-west-2

Get

  • get cloudcredential myAliasForCloudCredential vendorName
  • get cloudcredential myAliasForCloudCredential accessKeyId
  • get cloudcredential myAliasForCloudCredential secretAccessKey
  • get cloudcredential myAliasForCloudCredential region
  • get cloudcredential myAliasForCloudCredential alias

Show

  • show cloudcredential
  • show cloudcredential myAliasForCloudCredential

Delete

  • delete cloudcredential myAliasForCloudCredential

CloudConfiguration

Add

  • add cloudconfiguration serviceName=DYNAMODB alias=configAlias accelerateModeEnabled=true chunkedEncodingEnabled=true dualStackEnabled=true pathStyleAccessEnabled=true checksumValidationEnabled=true maxConnections=50 connectionMaxIdleTime=10s useIdleConnectionReaper=true socketTimeout=10s expectContinueEnabled=true connectionTimeout=10s connectionTimeToLive=10s connectionAcquisitionTimeout=10s useSystemPropertyValues=true numRetries=5 apiCallAttemptTimeout=2s

Set

  • set cloudconfiguration configAlias maxconnections=40
  • set cloudconfiguration configAlias maxconnections=40 numRetries=6

Get

  • get cloudconfiguration configAlias serviceName
  • get cloudconfiguration configAlias serviceName maxconnections
  • get cloudconfiguration configAlias maxconnections

Show

  • show cloudconfiguration
  • show cloudconfiguration configAlias

Delete

  • delete cloudconfiguration configAlias

Make DynamoDB requests

To make requests to DynamoDB using ColdFusion, you must the object that you had created using the service handle, getCloudService.

As an example, scan a table in DynamoDB, where you'd view all the items in the table and their attributes. To view the response, you must specify two parameters to the method Scan. These are:

  1. Body of the request
  2. Customization options

Request parameters

Create a struct that contains all the request parameters. To scan a DynamoDB table, you must pass these request parameters as a struct. For a list of all the request parameters, see Scan API.

tableName="MusicTableForDemo"
scanStruct = {
        "TableName": "#tableName#"
}

Customization options

The second parameter contains a few customizations that ColdFusion provides, which customizes the response.

This parameter is optional. If you do not want any of these customizations, you can pass an empty struct, or leave this parameter blank.

The supported optimizations are:

  • HasType: Boolean (Default: false)
  • CustomResponse: Boolean (Default: false)
  • BinaryFormat: Boolean (Default: false)
  • PreserveCFC: Boolean (Default: false)
  • CachedWithin: Used for caching
  • CachedAfter: Used for caching
  • CacheId: Used for caching
  • CacheRegion: Used for caching

hastype

Defines if the request data is assigned a type (for ex. S, L, M, etc). Values are True or False. Default value is False.

If specified as true, you will need to specify the types for all the datatypes being sent

If specified as false, ColdFusion will auto determine the type of the data. This is useful if you want to send a ColdFusion PDF. Spreadsheet, Query, CFC object to Dynamo but don’t want to manually serialize this data.

<cfscript>
    dynamo=getCloudService(application.awsCred, application.dynamoConf)
    movieName="Movies010"
    // Stage 1: create a table
    tableStruct={
        TableName : "#movieName#",
        KeySchema:[
            { AttributeName: "year", KeyType: "HASH"},
            { AttributeName: "title", KeyType: "RANGE"}
        ],
        AttributeDefinitions:[
            { AttributeName: "year", AttributeType: "N" },
            { AttributeName: "title", AttributeType: "S" }
        ],
        ProvisionedThroughput:{
            ReadCapacityUnits: 10,
            WriteCapacityUnits: 10
        }
    }
    dynamo.createTable(tableStruct)
    sleep(3000)
   // Stage 2: insert an item into the table
    putItemStruct={
        "TableName":"#movieName#",
        "Item":{
            "year": {"N": 2019},
            "title": {"S": "Golden"}
        },
        "ReturnValues": "NONE"
    }


    try{
        putItemResponse=dynamo.putItem(putItemStruct,{"hasType": true})
        writeOutput("Item inserted successfully in the table.")
        writeDump(putItemResponse)
    }
    catch (any e){
        writeDump(e)
    }
</cfscript>

customResponse

Defines if you want to see the response data with types (for ex. S, L, M, etc). Values are True or False. Default value is false

If true, the response from AWS won’t have the datatypes in the result struct. This is useful if you have sent a PDF/Spreadsheet/Query/CFC/Image object when writing data to DynamoDB. If you want to retrieve the data in ColdFusion object itself, you need to use the option customResponse: true.

If false, the response will show the various AWS DynmaoDB datatypes in response map.

<cfscript>
    // credentials and configuration 
    dynamo = getCloudService(application.awsCred,application.dynamoConf)
    tableName="MusicTableForDemo"
    scanStruct = {
        "TableName": "#tableName#"
    }
   scanResponse=dynamo.scan(scanStruct,{"customResponse":true})
   arr=scanResponse.items
   writeOutput("<b>List of songs</b>" & "")
   mapFunction=function(item){
       writeOutput(item.SongTitle & "<br/>")
    }
 
    arr.map(mapFunction)
</cfscript>

binaryFormat

The binaryFormat attribute customizes the data fields, which are binary in nature. When the value is true, it returns raw binary data. When the value is false, it returns Base64 Encoded data, as returned by DynamoDB. It works only when customResponse: true.

For example,

<cfscript>
    // credentials and configuration 
    dynamo = getCloudService(application.awsCred,application.dynamoConf)
    tableName="MusicTableForDemo"
    scanStruct = {
        "TableName": "#tableName#"
    }
    scanResponse=dynamo.scan(scanStruct,{"customResponse":true,"binaryFormat":true})
    
    arr=scanResponse.items
    writeOutput("<b>List of songs</b>" & "")
 
    mapFunction=function(item){
       writeOutput(item.SongTitle & "<br/>")
    }
 
    arr.map(mapFunction)
</cfscript>

preserveCFC

Defines if you want to send a CFC to a DynamoDB object. It works only when hasType: false. PreserveCFC must be set to true for deserializeCFC option to work while retrieving back CFC.

Employee.cfc

component accessors="true"
{
    property string empName;
    property numeric age;
    property string dept;
}

Helper.cfc

component accessors="true"
{
    public function putItemCFC(dynamoObject, itemStruct, hasType, preserveCfc) {
  result = dynamoObject.putItem(itemStruct, {"hasType": hasType, "preserveCFC": preserveCfc});
        if(result.HttpResponse.StatusCode eq 200) {
            writeOutput("Items written successfully<br />");
        } else {
            writeOutput("Failed to write the items<br />");
            writeDump(result)
        }
    }
}

SerializeCFC.cfm

<cfscript>
    empData = new Employee({empName="James", age=26, dept="000"});
    
    region1 = "ap-northeast-2";
    
    // credentials and configuration 
    dynamo = getCloudService(application.awsCred,application.dynamoConf)
    tableName = "Table_Test_CFC";
    TablePartitionKey = "OrderId";
    partitionKeyType = "N";
    TableSortKey = "OrderName";
    sortKeyType = "S";
    MyCFCKey = "OrderCFC";
    LocalSecondaryIndexSortKey = "OrderDateTime";
    secondaryIndexSortKeyType = "S";
    myComponent = createObject("Component", "helper");
    
    try {
        myComponent.deleteTable(dynamoUsWest1, tableName)
    } catch (any e) {
        writeDump(e)
    }
    
    try {
        myComponent.createTableWithLocalIndex(dynamoUsWest1, tableName, TablePartitionKey, partitionKeyType, TableSortKey, sortKeyType, LocalSecondaryIndexSortKey, secondaryIndexSortKeyType)
        sleep(20000);
    } catch (any e) {
        writeDump(e)
    }

    orderName = "MyOrder: ";
    strct_putItem = {
        "TableName": tableName,
        "Item":{
            "#TablePartitionKey#": 2,
            "#TableSortKey#": "MyOrder: 2",
            "CUSTOMER_KEY": "0001-23456",
            "OrderStatus": "CONFIRMED",
            "Year": 2012,
            "#LocalSecondaryIndexSortKey#": "2020/04/21",
            "#MyCFCKey#": empData
        }
    }
    try {
        result = dynamoObject.putItem(itemStruct, {"hasType": false, "preserveCFC": true});
        if(result.HttpResponse.StatusCode eq 200) {
            writeOutput("Items written successfully<br />");
        } else {
            writeOutput("Failed to write the items<br />");
            writeDump(result)
        }
    } catch (any e) {
        writeDump(e)
    }
</cfscript>

If you want to get back a CFC object from DynamoDB, use Preservecfc as true. Works only with customResponse: true. PreserveCFC must be true while the CFC object was stored to dynamodb.

CachedWithin

Defines the timeframe till which we want to cache our queries. After this time, cache items will not be available. It will be automatically purged.

There is an option Clear Dynamo Cache Now in Server Settings > Caching in ColdFusion Administrator. Clicking the button clears the Dynamo cache.

<cfscript>
    // credentials and configuration 
    dynamo = getCloudService(application.awsCred,application.dynamoConf)
    posdateresult=DateAdd("s", 30, now())
    tableName="MusicTableForDemo"
    scanStruct = {
        "TableName": "#tableName#"
    }
 
    scanResponse=dynamo.scan(scanStruct,{"customResponse":true, cachedWithin: "#createTimeSpan( 0, 0, 0, 30 )#"})
        
    arr=scanResponse.items
    writeOutput("<b>List of songs</b>" & "")
 
    mapFunction=function(item){
       writeOutput(item.SongTitle & "<br/>")
    }
 
    arr.map(mapFunction)
</cfscript>

CachedAfter

Defines the time after which we want to cache our queries. Cached items will be available only after this time.

There is an option Clear Dynamo Cache Now in Server Settings > Caching in ColdFusion Administrator. Clicking the button clears the Dynamo cache.

<cfscript>
    // credentials and configuration 
    dynamo = getCloudService(application.awsCred,application.dynamoConf)
    posdateresult=DateAdd("s", 30, now())
    tableName="MusicTableForDemo"
    scanStruct = {
        "TableName": "#tableName#"
    }
 
    scanResponse=dynamo.scan(scanStruct,{"customResponse":true,cachedAfter: posdateresult})
        
    arr=scanResponse.items
    writeOutput("<b>List of songs</b>" & "")
 
    mapFunction=function(item){
       writeOutput(item.SongTitle & "<br/>")
    }
 
    arr.map(mapFunction)
</cfscript>

Note:

For inline service config, the alias key is mandatory for query Caching to work properly.

Example

config = {

    "alias": "myConfig",    

    "serviceName" : "DYNAMODB"

};

For admin portal, alias is already mandatory.

Named parameters

ColdFusion DyanamoDB API’s also supports named parameters for its attributes

They are named:

  • Query
  • Options

Query parameter takes in the payload that needs to be sent.

For ex: For Scan of a table, it can be query={TableName: “yourTable”}

Options parameter is a struct mentioning all the options DynamoDB supports

For ex: hasType, customResponse, etc.

Options={hasType: true}

<cfscript>
    dynamo=getCloudService(application.awsCred, application.dynamoConf)
    movieName="Movies010"
    // Stage 1: create a table
    tableStruct={
        TableName : "#movieName#",
        KeySchema:[
            { AttributeName: "year", KeyType: "HASH"},
            { AttributeName: "title", KeyType: "RANGE"}
        ],
        AttributeDefinitions:[
            { AttributeName: "year", AttributeType: "N" },
            { AttributeName: "title", AttributeType: "S" }
        ],
        ProvisionedThroughput:{
            ReadCapacityUnits: 10,
            WriteCapacityUnits: 10
        }
    }
    dynamo.createTable(tableStruct)
    sleep(3000)
   // Stage 2: insert an item into the table
    putItemStruct={
        "TableName":"#movieName#",
        "Item":{
            "year": {"N": 2019},
            "title": {"S": "Golden"}
        },
        "ReturnValues": "NONE"
    }


    try{
        putItemResponse=dynamo.putItem(query = putItemStruct, options = {"hasType": true})
        writeOutput("Item inserted successfully in the table.")
        writeDump(putItemResponse)
    }
    catch (any e){
        writeDump(e)
    }
</cfscript>

Case-sensitive struct

A struct in ColdFusion is case-insensitive, while DynamoDB is case sensitive. To address this issue, a new struct type, casesensitive,  is introduced, which preserves case. You can pass a  request to DynamoDB in ColdFusion using the following:

  • Struct or
  • CaseSensitiveStruct

When dealing with DynamoDB methods, we recommend using case sensitive structs, as DynamoDB is case sensitive and there is no data loss due to case insensitivity.

For example,

cfscript>

    cred = {
        "credentialAlias" : "MyCredential",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
        "accessKeyId" : "XXXXXXXXXXXXXXXXX"
    }
       
    config = {
        "serviceName" = "DYNAMODB"                                                                       
    };

    dynamo = getCloudService(cred, config);
    strct = structNew("casesensitive");
    strct.TableName = "College"
    writedump(strct);
    result = dynamo.scan(strct, {} );
    writedump(result);
</cfscript>

Advanced datatype support

The following data types are supported:

  • CFC​: Use the option, preserveCFC as true.
  • Date​
  • BigDecimal​
  • PDF​: Use any cfpdf methods on the binary response object.
  • Query​: Use the method QueryNew on the AWS response object.
  • Image​
  • Spreadsheet​: Use the method SpreadSheetRead and pass the response object in binary.
  • XML​: Use the method DeserializeXML on the AWS response object.

The example below illustrates how you can tore and retrieve a spreadsheet object in DynamoDB using ColdFusion.

Spreadsheet

helperFuctions.cfc

component {
    function getLocalSecondaryIndex(partitionKey, sortKey, tableName) {
        localSecondaryIndex = [{
            "IndexName": "LocalIndex_" & tableName,
            "KeySchema": [
                {
                    "AttributeName": partitionKey,
                    "KeyType": "HASH"
                },
                {
                    "AttributeName": sortKey,
                    "KeyType": "RANGE"
                }
            ],
            "Projection": {
                "ProjectionType": "KEYS_ONLY"
            }
        }]

        return localSecondaryIndex;
    }

    function getGlobalSecondaryIndex(partitionKey, sortKey, tableName) {
        globalSecondaryIndex = [{
            "IndexName": "GlobalIndex_" & tableName,
            "KeySchema": [
                {
                    "AttributeName": partitionKey,
                    "KeyType": "HASH"
                },
                {
                    "AttributeName": sortKey,
                    "KeyType": "RANGE"
                }
            ],
            "Projection": {
                "ProjectionType": "KEYS_ONLY"
            },
            "ProvisionedThroughput": { 
                "ReadCapacityUnits": 10,
                "WriteCapacityUnits": 10
            }
        }]

        return globalSecondaryIndex;
    }

    function getCreateTableStruct(tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType) {
        createTable = {
            "TableName": tableName,
            "KeySchema": [
                {
                    "AttributeName": partitionKeyName,
                    "KeyType": "HASH"
                },
                {
                    "AttributeName": sortKeyName,
                    "KeyType": "RANGE"
                }
            ],
            "AttributeDefinitions": [
                {
                    "AttributeName": partitionKeyName,
                    "AttributeType": partitionKeyType
                },
                {
                    "AttributeName": sortKeyName,
                    "AttributeType": sortKeyType
                }
            ],
            "ProvisionedThroughput": {
                "ReadCapacityUnits": 10,
                "WriteCapacityUnits": 10
            },
            "StreamSpecification": { 
                "StreamEnabled": true,
                "StreamViewType": "NEW_AND_OLD_IMAGES"
            }
        }

        return createTable;
    }

    public function createTable(dynamoObject, tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType) {
        
        createTable = getCreateTableStruct(tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType);

        try {
            result = dynamoObject.createTable(createTable);
            if(result.HttpResponse.StatusCode eq 200) {
                writeOutput("Creating the table<br />");
            } else {
                writeOutput("Failed to create the table<br />");
            }
        } catch (any e) {
            writeDump(e)
        }
    }

    public function createTableWithLocalIndex(dynamoObject, tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType, localSortKey, localSortKeyType) {
        
        createTable = getCreateTableStruct(tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType);
        
        localIndex = getLocalSecondaryIndex(partitionKeyName, localSortKey, tableName)
        structInsert(createTable, "LocalSecondaryIndexes", localIndex);

        attributeDefinitionArray = structFind(createTable, "AttributeDefinitions")
        
        myStruct = structNew();
        myStruct["AttributeName"] = localSortKey;
        myStruct["AttributeType"] = localSortKeyType;
        arrayAppend(attributeDefinitionArray, myStruct)

        structUpdate(createTable, "AttributeDefinitions", attributeDefinitionArray);

        try {
            result = dynamoObject.createTable(createTable);
            if(result.HttpResponse.StatusCode eq 200) {
                writeOutput("Creating the table<br />");
            } else {
                writeOutput("Failed to create the table<br />");
            }
        } catch (any e) {
            writeDump(e)
        }
    }

    public function createTableWithGlobalIndex(dynamoObject, tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType, globalPartitionKey, globalPartitionKeyType, globalSortKey, globalSortKeyType) {
        
        createTable = getCreateTableStruct(tableName, partitionKeyName, partitionKeyType, sortKeyName, sortKeyType);
        
        globalIndex = getGlobalSecondaryIndex(globalPartitionKey, globalSortKey, tableName)
        structInsert(createTable, "GlobalSecondaryIndexes", globalIndex);

        attributeDefinitionArray = structFind(createTable, "AttributeDefinitions")
            
        myStruct = structNew();
        myStruct["AttributeName"] = globalPartitionKey;
        myStruct["AttributeType"] = globalPartitionKeyType;
        arrayAppend(attributeDefinitionArray, myStruct)

        myStruct1 = structNew();
        myStruct1["AttributeName"] = globalSortKey;
        myStruct1["AttributeType"] = globalSortKeyType;
        arrayAppend(attributeDefinitionArray, myStruct1)

        structUpdate(createTable, "AttributeDefinitions", attributeDefinitionArray);
        
        try {
            result = dynamoObject.createTable(createTable);
            if(result.HttpResponse.StatusCode eq 200) {
                writeOutput("Creating the table<br />");
            } else {
                writeOutput("Failed to create the table<br />");
            }
        } catch (any e) {
            writeDump(e)
        }
    }

    public function deleteTable(dynamoObject, tableName) {
        deleteTable = {
            TableName: tableName
        }

        try {
            result = dynamoObject.deleteTable(deleteTable);
            if(result.HttpResponse.StatusCode eq 200) {
                writeOutput("Deleting the table<br />");
            }
        } catch (any e) {
            if(e.ExceptionDetails.ExceptionCode eq "ResourceNotFoundException") {
                writeOutput("Deleting the table<br />");
            } else {
                writeOutput("Failed to delete the table<br />");
                writeDump(e)
            }
        }
    }

    public function createGlobalTable(dynamoObject, tableName, replicaRegion1, replicaRegion2) {
        createGlobalTable = {
            "GlobalTableName": tableName,
            "ReplicationGroup": [ 
                { 
                    "RegionName": replicaRegion1
                },
                {
                    "RegionName": replicaRegion2
                }
            ]
        }

      

    public function putItem(dynamoObject, itemStruct, hasType) {
        result = dynamoObject.putItem(itemStruct, {"hasType": hasType});
        if(result.HttpResponse.StatusCode eq 200) {
            writeOutput("Items written successfully<br />");
        } else {
            writeOutput("Failed to write the items<br />");
            writeDump(result)
        }
    }
}

sp.cfm

<cfscript>
    region1 = "us-west-1";
    cred_west1 = {
        "alias" : "alias",
        "vendorName" : "AWS",
        "region" : region1,
        "secretAccessKey" : "secret access key",
        "accessKeyId" : "access key"
    }
    config = {
        "serviceName" = "DYNAMODB"
    };
    dynamoUsWest1 = getCloudService(cred_west1, config);

    tableName = "Table_Cache_GetItem_1";
    TablePartitionKey = "OrderId";
    partitionKeyType = "N";
    TableSortKey = "OrderName";
    sortKeyType = "S";
    LocalSecondaryIndexSortKey = "OrderDateTime";
    secondaryIndexSortKeyType = "S";
    myComponent = createObject("Component", "helperFunctions");
    
    try {
        myComponent.deleteTable(dynamoUsWest1, tableName)
    } catch (any e) {
        writeDump(e)
    }
    
    try {
        myComponent.createTableWithLocalIndex(dynamoUsWest1, tableName, TablePartitionKey, partitionKeyType, TableSortKey, sortKeyType, LocalSecondaryIndexSortKey, secondaryIndexSortKeyType)
        sleep(20000);
    } catch (any e) {
        writeDump(e)
    }

    orderName = "MyOrder: ";
    strct_putItem = {
        "TableName": tableName,
        "Item":{
            "#TablePartitionKey#": 2,
            "#TableSortKey#": "MyOrder: ",
            "CUSTOMER_KEY": "0001-23456",
            "OrderStatus": "CONFIRMED",
            "Year": 2012,
            "#LocalSecondaryIndexSortKey#": "2020/04/21"
        }
    }
    for(i = 1; i < 5; i++) {
        structUpdate(strct_putItem.Item, "#TablePartitionKey#", i);
        structUpdate(strct_putItem.Item, "#TableSortKey#", orderName & i);

        myComponent.putItem(dynamoUsWest1, strct_putItem, "false")
        sleep(10000)
    }

    strct_getItem = {
        "TableName": tableName,
        "Key": {
            "#TablePartitionKey#": 2,
            "#TableSortKey#": "MyOrder: 2"
        },
        "ExpressionAttributeNames": {
            "##Yr": "Year"
        },
        "ProjectionExpression": "#TablePartitionKey#, ##Yr, OrderStatus",
        "ConsistentRead": false,
        "ReturnConsumedCapacity": "INDEXES"
    }
    try {
        result = dynamoUsWest1.getItem(strct_getItem, {CustomResponse: true, cachedWithin: "#createTimeSpan( 0, 0, 0, 45 )#"});
        if(
            (result.ConsumedCapacity.CapacityUnits == 0.5) &&
            (!structKeyExists(result.Item, "CUSTOMER_KEY")) &&
            (result.Item.OrderId == 2) &&
            (result.Item.Year == 2012) &&
            (structKeyExists(result.ConsumedCapacity, "Table"))
        ) {
            writeOutput("SUCCESS<br />");
        } else {
            writeOutput("FAIL<br />");
        }
    } catch (any e) {
        writeDump(e);
    }

    strct_putItem = {
        "TableName": tableName,
        "Item":{
            "#TablePartitionKey#": 2,
            "#TableSortKey#": "MyOrder: 2",
            "CUSTOMER_KEY": "0001-23456",
            "OrderStatus": "CONFIRMED",
            "Year": 2020,
            "#LocalSecondaryIndexSortKey#": "2020/04/21"
        }
    }
    myComponent.putItem(dynamoUsWest1, strct_putItem, "false")
    sleep(10000)

    try {
        result = dynamoUsWest1.getItem(strct_getItem, {CustomResponse: true, cachedWithin: "#createTimeSpan( 0, 0, 0, 45 )#"});
        if(
            (result.ConsumedCapacity.CapacityUnits == 0.5) &&
            (!structKeyExists(result.Item, "CUSTOMER_KEY")) &&
            (result.Item.OrderId == 2) &&
            (result.Item.Year == 2012) &&
            (structKeyExists(result.ConsumedCapacity, "Table"))
        ) {
            writeOutput("SUCCESS<br />");
        } else {
            writeOutput("FAIL<br />");
        }
    } catch (any e) {
        writeDump(e);
    }

    sleep(36000)

    try {
        result = dynamoUsWest1.getItem(strct_getItem, {CustomResponse: true, cachedWithin: "#createTimeSpan( 0, 0, 0, 45 )#"});
        if(
            (result.ConsumedCapacity.CapacityUnits == 0.5) &&
            (!structKeyExists(result.Item, "CUSTOMER_KEY")) &&
            (result.Item.OrderId == 2) &&
            (result.Item.Year == 2020) &&
            (structKeyExists(result.ConsumedCapacity, "Table"))
        ) {
            writeOutput("SUCCESS<br />");
        } else {
            writeOutput("FAIL<br />");
        }
    } catch (any e) {
        writeDump(e);
    }

    try {
        myComponent.deleteTable(dynamoUsWest1, tableName)
    } catch (any e) {
        writeDump(e)
    }
</cfscript>

DynamoDB functions

Table functions

Like all SQL and NoSQL databses, Amazon DynamoDB stores data in tables. The following are some of the operations that you can perform in a DynamoDB table.

Create a table

To create a table, use the function createTable. To create the table, you must provide the following:

  • Name of the table
  • Primary key: The primary key consists of one attribute (partition key) or two attributes (partition key and sort key). You must provide the attribute names, data types, and the role of each attribute: HASH (for a partition key) and RANGE (for a sort key).
  • Throughput settings: Specify the initial read and write throughput settings for the table.

The example below creates the table MusicForMe. The primary key consists of Artist (partition key) and SongTitle (sort key), with both the keys having a data type of String. The maximum throughput for this table is 10 read capacity units and 5 write capacity units.

<cfscript>
    cred = {
        "credentialAlias" : "alias",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "secret access",
        "accessKeyId" : "access key"
    }
    config = {
        "serviceName" = "DYNAMODB"
    }
    dynamo = getCloudService(cred, config)
    createTableStruct={
        TableName:"MusicForMe",
        AttributeDefinitions:[
            {AttributeName:"Artist",AttributeType:"S"},
            {AttributeName:"SongTitle",AttributeType:"S"}        
        ],
        KeySchema:[
            {AttributeName:"Artist",KeyType:"HASH"},
            {AttributeName:"SongTitle",KeyType:"RANGE"}
        ],
        ProvisionedThroughput:{
            ReadCapacityUnits: 10,
            WriteCapacityUnits: 10
        }
    }
    try{
        createTableResponse=dynamo.createTable(createTableStruct,{"customResponse": true})
        writeOutput("Table created successfully")
        writeDump(createTableResponse)
    }
    catch(any e){
        writeDump(e)
    }
</cfscript>

Describe a table

After creating the table, you may want to view the table information, like:

  • Attributes
  • Date of creation
  • Global Secondary Indexes
  • Local Secondary Indexes
  • Schema
  • Provisioned Throughput

Use the function describeTable for viewing the information of a table.

The example below describes the table MusicForMe, which was created in the previous section.

<cfscript>
  cred = {
    "credentialAlias" : "alias",
    "vendorName" : "AWS",
    "region" : "us-east-2",
    "secretAccessKey" : "secret access",
    "accessKeyId" : "access key"
  }
  config = {
    "serviceName" = "DYNAMODB"
  }
  dynamo = getCloudService(cred, config)
  
  // let's retrieve information about the table "MusicForMe"
  if (ArrayContains(listTablesResponse.TableNames,"MusicForMe")){
    describeTableStruct={
      "TableName":"YearlyProductCatalog"
    }
    describeTableResponse=dynamo.describeTable(describeTableStruct)
    writeDump(describeTableResponse)
  }
  else{
    writeOutput("Table not found")
  }
</cfscript>

Update a table

Updating a DynamoDB table involves either modifying the table's provisioned settings or read/write capacity modes or the global secondary indexes on the table.

For more information, see updateTable.

The example below updates the values of the read and the write capacity units of the table MusicForMe.

<cfscript>
    cred = {
        "credentialAlias" : "alias",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "secret access",
        "accessKeyId" : "access key"
    }
    config = {
        "serviceName" = "DYNAMODB"
    }
    dynamo = getCloudService(cred, config)

    tableName="MusicForMe"

    oldReadCapacityUnits=10
    oldWriteCapacityUnits=10
    newReadCapacityUnits=50
    newWriteCapacityUnits=50

    updateTableStruct={
        "TableName": "#tableName#",
        "ProvisionedThroughput": {
            "ReadCapacityUnits": "#newReadCapacityUnits#",
            "WriteCapacityUnits": "#newWriteCapacityUnits#"
        }
    }

    try{
        updateTableResponse=dynamo.updateTable(updateTableStruct)
        if(
            (updateTableResponse.HttpResponse.StatusCode==200) and
            (updateTableResponse.TableDescription.ProvisionedThroughput.ReadCapacityUnits==oldReadCapacityUnits) and
            (updateTableResponse.TableDescription.ProvisionedThroughput.WriteCapacityUnits==oldWriteCapacityUnits) and
            (updateTableResponse.TableDescription.TableStatus=="UPDATING")
        )
        {
            writeOutput("Old read and write values correct.")
        }
        else {
            writeOutput("Old read and write values incorrect.")
        }
    }
    catch(any e){
        writeDump(e)
    }
</cfscript>

Delete a table

Delete a table created previously by using the deleteTable function. Once you delete the table, you cannot restore it.

The example below deletes the table MusicForMe.

<cfscript>
  cred = {
    "credentialAlias" : "alias",
    "vendorName" : "AWS",
    "region" : "us-east-2",
    "secretAccessKey" : "secret access",
    "accessKeyId" : "access key"
  }
  config = {
    "serviceName" = "DYNAMODB"
  }
  dynamo = getCloudService(cred, config)
  tableName="MusicForMe"
  
  // delete the table
  deleteTableStruct={
    "TableName": "#tableName#"
  }
  try{
    deleteResponse=dynamo.deleteTable(deleteTableStruct)
    writeOutput("Table deleted successfully")
    writeDump(deleteResponse)
  }
  catch (any e){
    writeOutput("Unable to delete the table")
    writeDump(e)
  }
</cfscript>

List all tables

To see all the tables in your AWS region and account, use the function listTables.

This function returns an array of all the tables in your account. 

The example below returns the names of a maximum of 50 tables that exist in your account. You can change the limit accordingly.

<cfscript>
  cred={
    "credentialAlias" : "alias",
    "vendorName" : "AWS",
    "region" : "us-east-2",
    "secretAccessKey" : "secret access",
    "accessKeyId" : "access key"
  }

  conf={
    "serviceName"="DYNAMODB"
  }

  dynamo=getCloudService(cred, conf)
  params = {
    "Limit": 50
  }
  result = dynamo.ListTables(params);
  writeDump(result)
</cfscript>

Write data to the table

After creating the table, you must populate the table with data. The following are the operations you can perform in the table:

Insert an item

Insert an item to the table. Use the function putItem to insert a new item. If an item with the same key already exists in the table, it is replaced with the new item.

The example creates a table NewProductCatalog and inserts an item to the table.

<cfscript>
    cred={
        "credentialAlias" : "alias",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "secret access",
        "accessKeyId" : "access key"
    }
    
    conf={
        "serviceName"="DYNAMODB"
    }
    
    dynamo=getCloudService(cred, conf)
    tableName="NewProductCatalog"

    // Stage 1: Create a table
    createTableStruct={
        "TableName": "#tableName#",
        "KeySchema": [
            {
                "AttributeName": "id",
                "KeyType": "HASH"
            }
        ],
        "AttributeDefinitions": [
            {
                "AttributeName": "id",
                "AttributeType": "N"
            }
        ],
        "ProvisionedThroughput": {
            "ReadCapacityUnits": 10,
            "WriteCapacityUnits": 10
        }
    }

    dynamo.createTable(createTableStruct)
    sleep(20000)

    // Stage 2: Insert an item
    putItemStruct = {
        "TableName": "#tableName#",
        "Item":{
            "id": {
                "N": 250
            },
            "Title": {
                "S": "Hamlet"
            },
            "Price": {
                "N": "20"
            }
        },
        "ReturnValues": "ALL_OLD"
    }
    try{
        putItemResponse=dynamo.putItem(putItemStruct,{"hasType": true})
        writeOutput("Item inserted successfully")
        writeDump(putItemResponse)
    }
    catch (any e){
        writeDump(e)
    } 
</cfscript>

Update an item

After inserting the item,  you can update the item by using the updateItem function.

If an item with the specified key does not exist, the function creates an item. Otherwise, it modifies the existing item's attributes.

<cfscript>
    cred={
        "credentialAlias" : "alias",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "secret access",
        "accessKeyId" : "access key"
    }
    
    conf={
        "serviceName"="DYNAMODB"
    }
    
    dynamo=getCloudService(cred, conf)
    tableName="NewProductCatalog"
    // update the item that was inserted
    updateItemStruct={
        "TableName": "#tableName#",
        "Key": {
            "id": {
                "N": 250
            }
        },
        "UpdateExpression": "set Title = :val1",
        "ConditionExpression": "Price = :val2",
        "ExpressionAttributeValues": {
            ":val1": {"S": "Macbeth"},
            ":val2": {"N": "200"}
        },
        "ReturnValues": "ALL_NEW"
    }
    try{
        result = dynamo.updateItem(updateItemStruct, {"hasType": true})
        if(result.Attributes.Title.S == "Hamlet") {
            writeOutput("Title changed successfully<br/>")
        } else {
            writeOutput("Failed to change the title<br/>")
        }
    }
    catch (any e){
        writeDump(e)
    }        
</cfscript>

Delete an item

Delete an item in the table by using the deleteItem function.

<cfscript>
  cred = {
  "credentialAlias" : "alias",
  "vendorName" : "AWS",
  "region" : "us-east-2",
  "secretAccessKey" : "secret access",
  "accessKeyId" : "access key"
  }
  config = {
    "serviceName" = "DYNAMODB"
  }
  dynamo = getCloudService(cred, config)
  tableName="YearlyProductCatalog"
  // items to delete
  id=550
  title="hamlet"
  // delete the item in the table
  deleteItemStruct={
    "TableName":"#tableName#",
    "Key":{
      "id":{"N":"#id#"},
      "title":{"S":"#title#"}
    },
    "ReturnValues": "ALL_OLD"
  }
  try{
    deleteItemResponse=dynamo.deleteItem(deleteItemStruct,{"hasType": true})
    writeOutput("Item deleted successfully")
    writeDump(deleteItemResponse)
  }
  catch(any e){
    writeDump(e)
  }
</cfscript>

Read an item from the table

To read an item from a DynamoDB table, use the function getItem. You must provide the name of the table, along with the primary key of the item you want.

<cfscript>
    cred={
        "credentialAlias" : "alias",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "secret access",
        "accessKeyId" : "access key"
      }
    
    conf={
        "serviceName"="DYNAMODB"
    }

    dynamo=getCloudService(cred, conf)
    // create a table
    tableName="MusicForAll"
    createTableStruct={
         TableName:"#tableName#",
         AttributeDefinitions:[
             {AttributeName:"Artist",AttributeType:"S"},
             {AttributeName:"SongTitle",AttributeType:"S"}       
         ],
         KeySchema:[
             {AttributeName:"Artist",KeyType:"HASH"},
             {AttributeName:"SongTitle",KeyType:"RANGE"}
         ],
         ProvisionedThroughput:{
             ReadCapacityUnits: 10,
             WriteCapacityUnits: 10
         }
    }
    try{
         createTableResponse=dynamo.createTable(createTableStruct,{"customResponse": true})
         writeOutput("Table created successfully")
         writeDump(createTableResponse)
    }
    catch(any e){
         writeDump(e)
    }
    // insert an item to the table
    putItemStruct = {
        "TableName": "#tableName#",
        "Item":{
            "Artist":{"S":"Beatles"},
            "SongTitle":{"S":"Yesterday"},
            "Album":{"S":"Some album"}
        },
        "ReturnValues":"NONE"
    }
    try{
        putItemResponse=dynamo.putItem(putItemStruct,{"hasType": true})
        writeOutput("Item inserted successfully in the table.")
        writeDump(putItemResponse)
     }
     catch (any e){
        writeDump(e)
     }
     // get the item that was inserted
     getItemStruct={
        "TableName": "#tableName#",
        "Key":{
            "Artist":{"S":"Beatles"}
        }
     }
     try{
        getItemResponse=dynamo.putItem(getItemStruct)
        writeOutput("Item retrieved successfully in the table.")
        writeDump(putItemResponse)
     }
     catch (any e){
        writeDump(e)
     }
</cfscript>

Update an item in the table

After you've inserted an item in the previous section, you can update the item. In the example below, we have updated the Album to a new value.

<cfscript>
    cred={
        "credentialAlias" : "alias",
        "vendorName" : "AWS",
        "region" : "us-east-2",
        "secretAccessKey" : "secret access",
        "accessKeyId" : "access key"
    }
    
    conf={
        "serviceName"="DYNAMODB"
    }
    
    dynamo=getCloudService(cred, conf)
    tableName="MusicForAll"

    // update the item in the table
    updateItemStruct={
        "TableName":"#tableName#",
        "Key":{
            "Artist":{"S":"Beatles"},
            "SongTitle":{"S":"Yesterday"}         
        },
        "UpdateExpression": "SET Album = :newval",
        "ExpressionAttributeValues":{
            ":newval": {"S": "Some other title"}
        },
        "ReturnValues": "ALL_NEW"
    }

    try{
        updateItemResponse=dynamo.updateItem(updateItemStruct,{"hasType":true,"customResponse":true})
        writeDump(updateItemResponse)
    }
    catch(any e){
        writeDump(e)
    }
</cfscript>