6

How I can get JSON object in response from DynamoDB ? I store data in DB as array of object in format JSON. I have next mapping template request

{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
  "userId": {
    "S":  "$context.identity.username"
  }
},
  #set( $attrs = $util.dynamodb.toMapValues($ctx.args))
  #set( $attrs.categories = $util.dynamodb.toDynamoDB($ctx.args.categories))

  "attributeValues": $util.toJson($attrs)
}

and mapping template response

#set( $result = $ctx.result)
#set( $result.categories = $util.parseJson($ctx.result.categories))
$util.toJson($result)

but I got response in the format DynamoDB JSON

"createItem": {
      "title": "Test 1",
       "categories": "[{name=food, id=2}, {name=eat, id=1}]"
    }

in dynamoDB date save as

"categories": {
    "L": [
      {
        "M": {
          "id": {
            "S": "2"
          },
          "name": {
            "S": "food"
          }
        }
      },
      {
        "M": {
          "id": {
            "S": "1"
          },
          "name": {
            "S": "eat"
          }
        }
      }
    ]
  }

How parse it to normal JSON or object ?

Volodymyr Zh
  • 317
  • 7
  • 19

1 Answers1

4

In your current mapping template, you are storing categories as a "S" in DDB which means string and this is why you are getting a stringified version of the DynamoDB list. Assuming you are running a mutation that looks something like this:

mutation {
  create(input: { title: "Test 1", categories: [{ name: "category 1" }] }) {
    title
    categories {
      name
    }
  }
}

Then you should change your mapping template to this:

{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
    "userId": {
      "S":  "$context.identity.username"
    }
  },
  "attributeValues": $util.toJson($ctx.args)
}

The above template can be used if you want to store the data as DynamoDB Lists and Maps. If you are instead trying to store your JSON as a JSON stringified blob in a DynamoDB "S" attribute but without the L's and M's then instead change your template to this:

{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
    "userId": {
      "S":  "$context.identity.username"
    }
  },
    #set( $attrs = $util.dynamodb.toMapValues($ctx.args))

    ## NOTE: The $util.toJson instead of the dynamodb version which adds L, M, S, etc
    #set( $attrs.categories = { "S":  "$util.toJson($ctx.args.categories)"})
    "attributeValues": $util.toJson($attrs)
}

And then in the response mapping template, you will need to parse the JSON to returned structured JSON instead of a JSON stringified string.

## Get the result and parse categories into structured objects.
#set( $result = $ctx.result)
#set( $result.categories = $util.parseJson($ctx.result.categories))

## Return the full JSON payload
$util.toJson($result)

EDIT (More Details):

I have these schema parts (note this is not complete):

type Category {
    name: String
}

input CategoryInput {
    name: String
}

input CreatePostInput {
    title: String
    categories: [CategoryInput]
}

type Post {
    id: ID!
    title: String
    categories: [Category]
}

type Mutation {
    createPost(input: CreatePostInput!): Post
}

And this request mapping template exactly:

## Mutation.createPost request mapping template
#set( $attrs = $util.dynamodb.toMapValues($ctx.args.input))
#set( $attrs.categories = { "S":  "$util.toJson($ctx.args.input.categories)"})

{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
    "id": $util.dynamodb.toDynamoDBJson($util.autoId()),
  },
  "attributeValues": $util.toJson($attrs),
  "condition": {
    "expression": "attribute_not_exists(#id)",
    "expressionNames": {
      "#id": "id",
    },
  },
}

And this response mapping template

## Mutation.createPost response mapping template
## Get the result and parse categories into structured objects.
#set( $result = $ctx.result)
#set( $result.categories = $util.parseJson($ctx.result.categories))

## Return the full JSON payload
$util.toJson($result)

I was then able to run this query:

mutation {
  createPost(input: {
    title: "Hello, world!",
    categories: [
      {
        name: "cat1"
      }
    ]
  }) {
    id
    title
    categories {
      name
    }
  }
}

and got this response:

{
  "data": {
    "createJSONTest2": {
      "id": "c72ff226-0d67-41c4-9c47-784955a64bc5",
      "title": "Hello, world!",
      "categories": [
        {
          "name": "cat1"
        }
      ]
    }
  }
}

when I go to the DynamoDB console this is stored as the category attribute

[{"name":"cat1"}]

It appears this is all working correctly. Lmk if you need further help debugging.

mparis
  • 3,623
  • 1
  • 17
  • 16
  • Thanks !! The second variant works for me. but when I use `$util.parseJson($ctx.result.categories)` I got result `"categories": "[{id=1, name=category 1}, {id=2, name=category 2}]" ` It's not JSON .. – Volodymyr Zh Jul 23 '18 at 18:10
  • $util.parseJson() returns a map which should only become valid JSON once you call $util.toJson() on it. Can you share your response mapping template that yielded the above? The last line **$util.toJson($result)** I believe should JSONify the entire nested structure. – mparis Jul 23 '18 at 19:51
  • I use response template witch you wrote. `#set( $result = $ctx.result) #set( $result.categories = $util.parseJson($ctx.result.categories)) $util.toJson($result)` – Volodymyr Zh Jul 23 '18 at 20:16
  • ok thank you. let me investigate as this is not desired behavior. – mparis Jul 23 '18 at 20:26
  • Hmm I was able to reproduce this and it looks like it is working correctly for me. I will update the answer with more details including the schema and query that I ran so you can debug. – mparis Jul 23 '18 at 20:43
  • It's very strange. I have very similar schema and template and in DynamoDb console, data stored as JSON `categories String: [{\"id\":\"2\",\"name\":\"food\"},{\"id\":\"1\",\"name\":\"eat\"}]` But I can't get data from request with $ctx.args`.input`. Without `.input` - ok . Thank you. I will be search solutions – Volodymyr Zh Jul 24 '18 at 06:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/176630/discussion-between---and-mparis). – Volodymyr Zh Jul 24 '18 at 10:22
  • I found my mistake, I had wrong type in schema for category. My type was string but need to be List. – Volodymyr Zh Jul 24 '18 at 11:55
  • Yep i was saving timestamps as `{ "S" : { "S" : "2020-04-29T06:47:11.097Z" }}` which is dynamo format. Changed "attributeValues" from `$util.dynamodb.toMapValuesJson($attributes)` to `$util.toJson($attributes)` – mewc Apr 29 '20 at 06:58