Wy do I get an error when issuing QueryCommand in nodejs sdk v3? - amazon-web-services

I am using aws node.js sdk v3. I want to do a simple query on the table using dynamodbdocumentclient. I wanted to do just a test if query works on a simple music table which is provided by aws developer guide.
const data = await this.dynamoDBDocumentClient.send(new QueryCommand({
TableName: "Music",
KeyConditionExpression: "#Artist = :artist",
ExpressionAttributeName: {
"#Artist": "artist"
},
ExpressionAttributeValues: {
":artist": "Acme Band"
},
});
But when I call this I get:
TypeError: Cannot read property '0' of undefined
When I do a simple:
const data = await.this.dynamoDBDocumentClient.send(newScanCommand({TableName: "Music"});
I am getting the correct response:
{
"AlbumTitle": {
"S": "Somewhat Famous"
},
"Awards": {
"N": "1"
},
"Artist": {
"S": "No One You Know"
},
"SongTitle": {
"S": "Call Me Today"
}
},
{
"AlbumTitle": {
"S": "Songs About Life"
},
"Awards": {
"N": "10"
},
"Artist": {
"S": "Acme Band"
},
"SongTitle": {
"S": "Happy Day"
}
},
{
"AlbumTitle": {
"S": "Songs About Sadness"
},
"Awards": {
"N": "8"
},
"Artist": {
"S": "Acme Band"
},
"SongTitle": {
"S": "Sad Day"
}
}
],
"ScannedCount": 3
}```
What am I doing wrong? I don't get why I am getting en error.

I just had the same problem, and according to this guide, the ExpressionAttributeValues must follow the DynamoDB JSON schema, being so, if your artist is a string, it should be:
...
ExpressionAttributeValues: {
":artist": {S: "Acme Band"}
},
...
Adding this, worked for me.

I think you simply have a typo here:
ExpressionAttributeName
It should be ExpressionAttributeNames (missing last letter).
However AWS-SDK is not very helpful with error messages. Use TypeScript to avoid similar issues, since it will show you, that you are using wrong field name basing on types definition.

Related

DynamoDB / ApiGateWay UpdateItem method returning serializedException

The bounty expires in 14 hours. Answers to this question are eligible for a +50 reputation bounty.
Wesley van S is looking for an answer from a reputable source:
Should be an easy question for someone experienced in AWS Api Gateway / DynamoDB
Quite new to AWS / DynamoDB. I'm trying to make a UpdateItem method work on my API. I have a Resource with the path x/users/update/{wallet} with wallet being the key.
In here I set a UpdateItem method just like I have all my other Query / Scan / PutItem methods. this has the following mapping template:
{
"TableName": "Users",
"KeyConditionExpression": "wallet = :v1",
"ExpressionAttributeValues": {
":v1": {
"S": "$input.params('wallet')"
}
":v2": {
"S": $input.json('$.username')
}
}
"UpdateExpression": "set username = :v2",
"ReturnValues": "UPDATED_NEW"
}
This returns a 200 with "__type": "com.amazon.coral.service#SerializationException"
Pretty sure this is related to my Mapping template but can't figure out what. Looked at the https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html and think I used the Sample Request in there as a template to test it like this
{
"TableName": "Users",
"Key": {
"wallet": {
"S": "$input.params('wallet')"
},
},
"UpdateExpression": "SET username = :val1",
"ExpressionAttributeValues": {
":val1": {
"S": "$input.path('$.username')"
},
},
"ReturnValues": "ALL_NEW"
}
payload is just as simple as
{"username":"test"}
but that gives the same result.
Any ideas on what i'm doing wrong here?
Cheers!
EDIT:
PutItem looks like this:
{
"TableName": "Users",
"Item": {
"userId": {
"S": "$context.requestId"
},
"date": {
"S": "$input.path('$.date')"
},
"wallet": {
"S": "$input.path('$.wallet')"
}
}
}
It looks like you're not accessing the record to update with the key of your table. Unlike SQL, you cannot access a record to update with any field in DynamoDB. You have to use your key depending on how you defined it (PK or PK + SK).
Given that you have both PK and SK defined, you need them both in your key input. Together they form a composite primary key. The below input should work for you.
{
"TableName": "Users",
"Key": {
"wallet": {
"S": "$input.params('wallet')"
},
"date": {
"S": "$input.params('date')"
},
},
"UpdateExpression": "SET username = :val1",
"ExpressionAttributeValues": {
":val1": {
"S": "$input.path('$.username')"
},
},
"ReturnValues": "ALL_NEW"
}
DynamoDB UpdateItem takes a Key not a KeyConditionExpression
This one below is correct:
{
"TableName": "Users",
"Key": {
"wallet": {
"S": "$input.params('wallet')"
},
},
"UpdateExpression": "SET username = :val1",
"ExpressionAttributeValues": {
":val1": {"S": "$input.params('$.username')"},
},
"ReturnValues": "ALL_NEW"
}
You also state your payload is simply
{"username":"test"}
You must also specify wallet as this is the key

Access an Array Item by index in AWS Dynamodb Query Results "Items" in Step Function

I have this dynamodb:Query in my step function:
{
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:dynamodb:query",
"Next": "If nothing returned by query Or Study not yet Zipped",
"Parameters": {
"TableName": "TEST-StudyProcessingTable",
"ScanIndexForward": false,
"Limit": 1,
"KeyConditionExpression": "OrderID = :OrderID",
"FilterExpression": "StudyID = :StudyID",
"ExpressionAttributeValues": {
":OrderID": {
"S.$": "$.body.order_id"
},
":StudyID": {
"S.$": "$.body.study_id"
}
}
},
"ResultPath": "$.processed_files"
}
The results comes in as an array called Items which is nested under my ResultPath
processed_files.Items:
{
"body": {
"order_id": "1001",
"study_id": "1"
},
"processed_files": {
"Count": 1,
"Items": [
{
"Status": {
"S": "unzipped"
},
"StudyID": {
"S": "1"
},
"ZipFileS3Key": {
"S": "path/to/the/file"
},
"UploadSet": {
"S": "4"
},
"OrderID": {
"S": "1001"
},
"UploadSet#StudyID": {
"S": "4#1"
}
}
],
"LastEvaluatedKey": {
"OrderID": {
"S": "1001"
},
"UploadSet#StudyID": {
"S": "4#1"
}
},
"ScannedCount": 1
}
}
My question is how do i access the items inside this array from a choice state in a step function?
I need to query then decide something based on the results by checking the item in a condition in a choice state.
The problem is that since this is an array I can't access it using regular JsonPath (like with Items.item), and in my next step the choice condition does NOT accept an index like processed_files.Items['0'].Status
Ok so the answer was so simple all you need to do is use a number instead of string for the array index like this.
processed_files.Items[0].Status
I was originally mislead by an error I received which said that it expected a ' or '[' after the first '['. I mistakenly thought this meant it only accepts strings.
I was wrong, it works like any other array.
I hope this helps somebody one day.

How to remove JSON schema from the response from AWS API gateway?

I'm using AWS API gateway as a proxy to DynamoDb as per this documentation:
https://aws.amazon.com/blogs/compute/using-amazon-api-gateway-as-a-proxy-for-dynamodb/
When tested in the API gateway, the result is as below:
{
"Count": 6,
"Items": [
{
"mini_description": {
"S": "A veg sandwich"
},
"item_description": {
"S": "A veg sandwich filled with a lot of healthy vegetables"
},
"id": {
"S": "6d0e0870-......-c5ccfbc0424c"
},
"image_url": {
"S": "https://......png"
},
"price": {
"N": "25"
},
"name": {
"S": "Veg Sandwich"
},
"item_type": {
"S": "Main Dish"
}
},
{
"mini_description": {
"S": "A normal hot coffee"
},.....
I need it in the following format:
{
"Count": 6,
"Items": [
{
"mini_description": "A veg sandwich",
"item_description": "A veg sandwich filled with a lot of healthy vegetables",
"id": "6d0e0870-.......-c5ccfbc0424c",
"image_url": "https://.......png",
"price": 25,
"name": "Veg Sandwich",
"item_type": "Main Dish"
},
{
"mini_description": "A normal hot coffee",............
Is there any procedure to change this through Integration response of API gateway?
I have achieved this by using the following mapping template in integration response of the GET method in API gateway:
#set($inputRoot = $input.path('$'))
{
"Items": [
#foreach($elem in $inputRoot.Items)
{
"mini_description" : "$elem.mini_description.S",
"item_description" : "$elem.item_description.S",
"id" : "$elem.id.S",
"image_url" : "$elem.image_url.S",
"price" : $elem.price.N,
"name" : "$elem.name.S",
"item_type" : "$elem.item_type.S"
}#if($foreach.hasNext),#end
#end
]
}
There is no way to unmarshall the data if you have API gateway going directly to DynamoDB. However, you can add a Lambda function in between API gateway and DynamoDB, and then use the unmarshall function from Javascript SDK (or any other preferred language) to remove the DynamoDB JSON elements.

Sort/sequence AWS API gateway output

tl;dr looking to sort API output from AWS API gateway
I have a dynamoDB table 'NP' with key 'id' and GSI 'atype-index'. There is no sort key.
Also, I have API gateway mapping template
{
"TableName": "NP",
"IndexName": "atype-index",
"KeyConditionExpression": "atype = :v1",
"ExpressionAttributeValues": {
":v1": {
"S": "$input.params('atype')"
}
}
}
The mapping template will extract all values in the table with GSI key matching the specified string. However, the values are extracted in no particular order, for example, the API could be:
{
"Count": 3,
"Items": [
{
"apiUrl": {
"S": “ee”
},
"webTitle": {
"S": “dd”
},
"atype": {
"S": “type”
},
"id": {
"S": "1a"
}
},
{
"apiUrl": {
"S": “dd”
},
"webTitle": {
"S": “cc”
},
"atype": {
"S": "atype"
},
"id": {
"S": “3a”
}
},
{
"apiUrl": {
"S": “cc”
},
"webTitle": {
"S": “bb”
},
"atype": {
"S": "atype"
},
"id": {
"S": “2a”
}
}
],
"ScannedCount": 3
}
How do I sort the above output by the key "apiURL" for example?
What you described is exactly what a sort key would accomplish, assuming you're using the Query API (rather than Scan).
If you add a sort key, the results will be sorted based on the sort key when querying a given partition key.
Edit: Just to clarify, there is no way to do the sort at the API GW layer in a mapping template.
You could put a Lambda function in between API GW and DDB if the overhead is worth it for your use case. Otherwise, probably better to just sort at client side.

Unable to map a list of numbers in API-Gateway

How do I create a mapping for a list of numbers object in API gateway? I am trying to post a list of integers using POST request. I tried working with NS attribute but got the Error.
Error:
{
"__type": "com.amazon.coral.service#SerializationException"
}
However, it works well when I have N attribute and post a single integer value.
Is there any way to resolve this issue?
I believe you are trying to map your request payload to DynamoDB JSON String. You can apply a velocity template like this one,
{
"TableName":"ABC",
"Item": {
"id": {
"S": "$context.requestId"
},
"name": {
"S": "$input.path('$.name')"
},
"price": {
"L": [
#set($prices=$input.path('$.price'))
#foreach($p in $prices)
{
"N": "$p"
}#if ($velocityCount < $prices.size()), #end
#end
]
}
}
}
Method Request Body:
{
"name":"Test",
"price": [1, 2, 3]
}
Endpoint Request Body:
{
"TableName": "ABC",
"Item": {
"id": {
"S": "test-invoke-request"
},
"name": {
"S": "Test"
},
"price": {
"L": [
{
"N": "1"
},
{
"N": "2"
},
{
"N": "3"
}
]
}
}
}