5
votes

I am trying to figure out how to insert dynamic data into a dynamodb table via an API Gateway in AWS. Currently I have a dynamodb table and an API endpoint setup that accepts a POST like so.

POST https://{unique-id}.execute-api.us.east-1.amazonaws.com/notification/events

{
  "reference_number": 99,
  "purchase_date": "1/1/2017"
}

I've setup a body mapping template in the API gateway to massage the data into the dynamodb.

{ 
    "TableName": "Events",
    "Item": {
        "reference_number": {
            "N": "$input.path('$.reference_number')"
        },
        "purchase_date": {
            "S": "$input.path('$.purchase_date')"
        }
    }
}

The above works and saves to the table.

Suppose I add the event hash to my json (which can change based on events).

{
  "reference_number": 99,
  "purchase_date": "1/1/2017",
  "event": {
     "name": "purchase",
     "items": [1,3,6],
     "info": {
       "currencyID": "USD",
       "countryID": "US"
     }
  }
}

How do I save the event attribute to a Map in dynamodb using the API Gateway Body mapping template syntax?

{ 
    "TableName": "Events",
    "Item": {
        "reference_number": {
            "N": "$input.path('$.reference_number')"
        },
        "purchase_date": {
            "S": "$input.path('$.purchase_date')"
        },
        "event":{
            "M": "$input.path('$.event')"
        }
    }
}

The above template gives me the following error. "Expected map or null"

3
Have you tried "event": { "M": "$input.json('$.event')" }? - Khalid T.
No luck, I get a serialization exception. "__type":"com.amazon.coral.service#SerializationException" - MikeV

3 Answers

4
votes

It looks like DynamoDB API actually requires the value of an 'M' attribute to be a Map of String -> AttributeValue. Unfortunately you can't pass the raw JSON. You'll have to manually map the whole event object to make the DDB API happy.

http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_AttributeValue.html#DDB-Type-AttributeValue-M

One possible workaround would be to stringify the event object and write it as type S but that would of course require the reader to expect that behavior.

{ 
    "TableName": "Events",
    "Item": {
        "reference_number": {
            "N": "$input.path('$.reference_number')"
        },
        "purchase_date": {
            "S": "$input.path('$.purchase_date')"
        },
        "event":{
            "S": "$util.escapeJavaScript($input.json('$.event'))"
        }
    }
}
1
votes

As it seems you finally did. I reckon the best option is to create a simple lambda function between you API and dynamoDB. Leaving the mapping work up to the aws-sdk.

In that case, the body mapping template in the API gateway would be as simple as this:

$input.body

And the function won't be much more complicated. I used a javascript function:

var AWS = require("aws-sdk");

var docClient = new AWS.DynamoDB.DocumentClient();
var tableName = "tableName";

var saveData = function (data) {

  var params = {
    TableName: tableName,
    Item: data
  };

  docClient.put(params, function (err, data) {
    if (err) {
      console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
    } else {
      console.log("Added item:", JSON.stringify(data, null, 2));
    }
  });

};

exports.handler = function (event) {
  try {
    console.log("Processing event: ", event);
    saveData(event);
  } catch (e) {
    console.error("Processed unsuccessfully", e, e.stack);
  }
};

http://docs.aws.amazon.com/amazondynamodb/latest/gettingstartedguide/GettingStarted.NodeJs.03.html

0
votes

For my requirement (saving input body), the mapping template is

"rawdata": {
            "M": $input.body
        }

Note that there are no quotes for input body.

And the data should be in Dynamodb format, for eg

{"username":{"S":"Vishnu"}}

You could use js lib like dynamodb-marshaller to convert json to Dynamodb format. Hope this helps.