Update Item

In this step, we will create a new Mutation that allows updating an item in the DynamoDB table. We will use DynamoDB’s UpdateItem.

Update a post

  1. Select the Schema tab
  • Add the code below to the Mutation type to add a new mutation called updatePost
  • Then click Save Schema
    updatePost(
        id: ID!,
        author: String!,
        title: String!,
        content: String!,
        url: String!
    ): Post

CreateAddPostResolver

  1. In the Resolvers pane on the right, find the updatePost field of type Mutation, then select Attach

CreateAddPostResolver

  1. On the Attach resolver configuration page:
  • Resolver type: Select Unit resolver
  • Resolver runtime: Select Velocity Template Language (VTL)
  • Data source name: Select PostDynamoDBTable
  • Scroll down to the bottom and click the orange Create button.

CreateGetResolvers

  1. After creation, you will be taken to the Resolver code editor page. Here:
  • Delete the default code and paste the content below into the Configure the request mapping template section:
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },
    "update" : {
        "expression" : "SET author = :author, title = :title, content = :content, #url = :url ADD version :one",
        "expressionNames": {
            "#url" : "url"
        },
        "expressionValues": {
            ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
            ":title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
            ":content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
            ":url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
            ":one" : { "N": 1 }
        }
    }
}

CreateAddPostResolver

This Resolver is using a DynamoDB UpdateItem, which is significantly different from a PutItem operation. Instead of writing the entire item, you are only asking DynamoDB to update certain attributes. To do this, you use DynamoDB Update Expressions. You can see the “expression” section under “update”. It tells DynamoDB to set the author, title, content, and url attributes, and then increment the version field. The Expression contains placeholders whose names begin with a colon, which are then defined in the “expressionValues” section. Finally, DynamoDB has reserved words that cannot appear in expressions. For example, url is a reserved word, so to update the url field, you can use placeholder names and define them in the “expressionNames” section.

  1. Scroll down, delete the default code, and paste the content below into the Configure the response mapping template section:
#if($context.error)
  $util.error($context.error.message, $context.error.type)
#end
$util.toJson($context.result)

CreateAddPostResolver

  1. Click Save in the corner of the page to save

CreateAddPostResolver

  1. Select the Queries tab on the left menu to call the API to update a post
  • Delete the default code in the editor, paste the script below into the Queries section, then click the Run button (the orange Play button)
mutation updatePost {
  updatePost(
    id:"123"
    author: "A new author"
    title: "An updated author!"
    content: "Now with updated content!"
    url: "https://aws.amazon.com/appsync/"
  ) {
    id
    author
    title
    content
    url
    ups
    downs
    version
  }
}

CreateAddPostResolver

  1. The returned result is similar to the following:
{
  "data": {
    "updatePost": {
      "id": "123",
      "author": "A new author",
      "title": "An updated author!",
      "content": "Now with updated content!",
      "url": "https://aws.amazon.com/appsync/",
      "ups": 1,
      "downs": 0,
      "version": 2
    }
  }
}

CreateAddPostResolver

After completing the update, the version attribute is incremented by one because we requested AWS AppSync and DynamoDB to add one to the version attribute upon each update.

When you use the updatePost mutation as above, two problems will occur:

  • If you only want to update a single field, you must update all fields.
  • If two people are modifying the object, you could lose information. Follow the next steps to fix these issues.
  1. Select the Schema tab on the left menu
  • Replace the code of the updatePost mutation with the content below:
  • Then click Save Schema
    updatePost(
        id: ID!,
        author: String,
        title: String,
        content: String,
        url: String,
        expectedVersion: Int!
    ): Post

CreateAddPostResolver

  1. In the Resolvers pane on the right, find the updatePost field of type Mutation, then click on the data source name PostDynamoDBTable to edit the current Resolver.

CreateAddPostResolver

  1. On the code editor page:
  • Delete the old code and paste the content below into the Configure the request mapping template section:
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },

    ## Set up some space to keep track of things you're updating **
    #set( $expNames  = {} )
    #set( $expValues = {} )
    #set( $expSet = {} )
    #set( $expAdd = {} )
    #set( $expRemove = [] )

    ## Increment "version" by 1 **
    $!{expAdd.put("version", ":one")}
    $!{expValues.put(":one", { "N" : 1 })}

    ## Iterate through each argument, skipping "id" and "expectedVersion" **
    #foreach( $entry in $context.arguments.entrySet() )
        #if( $entry.key != "id" && $entry.key != "expectedVersion" )
            #if( (!$entry.value) && ("$!{entry.value}" == "") )
                ## If the argument is set to "null", then remove that attribute from the item in DynamoDB **

                #set( $discard = ${expRemove.add("#${entry.key}")} )
                $!{expNames.put("#${entry.key}", "$entry.key")}
            #else
                ## Otherwise set (or update) the attribute on the item in DynamoDB **

                $!{expSet.put("#${entry.key}", ":${entry.key}")}
                $!{expNames.put("#${entry.key}", "$entry.key")}
                $!{expValues.put(":${entry.key}", { "S" : "${entry.value}" })}
            #end
        #end
    #end

    ## Start building the update expression, starting with attributes you're going to SET **
    #set( $expression = "" )
    #if( !${expSet.isEmpty()} )
        #set( $expression = "SET" )
        #foreach( $entry in $expSet.entrySet() )
            #set( $expression = "${expression} ${entry.key} = ${entry.value}" )
            #if ( $foreach.hasNext )
                #set( $expression = "${expression}," )
            #end
        #end
    #end

    ## Continue building the update expression, adding attributes you're going to ADD **
    #if( !${expAdd.isEmpty()} )
        #set( $expression = "${expression} ADD" )
        #foreach( $entry in $expAdd.entrySet() )
            #set( $expression = "${expression} ${entry.key} ${entry.value}" )
            #if ( $foreach.hasNext )
                #set( $expression = "${expression}," )
            #end
        #end
    #end

    ## Continue building the update expression, adding attributes you're going to REMOVE **
    #if( !${expRemove.isEmpty()} )
        #set( $expression = "${expression} REMOVE" )

        #foreach( $entry in $expRemove )
            #set( $expression = "${expression} ${entry}" )
            #if ( $foreach.hasNext )
                #set( $expression = "${expression}," )
            #end
        #end
    #end

    ## Finally, write the update expression into the document, along with any expressionNames and expressionValues **
    "update" : {
        "expression" : "${expression}"
        #if( !${expNames.isEmpty()} )
            ,"expressionNames" : $utils.toJson($expNames)
        #end
        #if( !${expValues.isEmpty()} )
            ,"expressionValues" : $utils.toJson($expValues)
        #end
    },

    "condition" : {
        "expression"       : "version = :expectedVersion",
        "expressionValues" : {
            ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
        }
    }
}

CreateAddPostResolver

  • Then click Save in the corner of the page.
  1. Select the Queries tab on the left menu to call the API to update a post
  • Delete the code in the editor, paste the script below into the Queries section, then click the Run button (the orange Play button)
mutation updatePost {
  updatePost(
    id:123
    title: "An empty story"
    content: null
    expectedVersion: 2
  ) {
    id
    author
    title
    content
    url
    ups
    downs
    version
  }
}

CreateAddPostResolver

  1. The returned result is similar to the following:
{
  "data": {
    "updatePost": {
      "id": "123",
      "author": "A new author",
      "title": "An empty story",
      "content": null,
      "url": "https://aws.amazon.com/appsync/",
      "ups": 1,
      "downs": 0,
      "version": 3
    }
  }
}

CreateAddPostResolver

In this request, we asked AWS AppSync and DynamoDB to only update the title and content fields, keeping the other fields the same except for incrementing the version field.

  1. Try clicking the Run button again, the returned result is similar to the following:

CreateAddPostResolver

The request failed because the condition expression evaluated to false:

  • The first time it ran, the value of the version field of the post in DynamoDB was 2, which matched expectedVersion. The request succeeded, meaning the version field in DynamoDB was incremented to 3.
  • The second time it ran, the value of the version field of the post in DynamoDB was 3, which did not match expectedVersion.

Update vote counts

We will create two new mutations to update the ups and downs fields to record upvotes or downvotes for the post.

  1. Select the Schema tab
  • Add the code below to the Mutation type to add two new mutations called upvotePost and downvotePost
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
  • Then click Save Schema

  • Next, in the Resolvers pane on the right, find the upvotePost field of type Mutation, then select Attach

CreateAddPostResolver

  1. On the Attach resolver configuration page:
  • Resolver type: Select Unit resolver
  • Resolver runtime: Select Velocity Template Language (VTL)
  • Data source name: Select PostDynamoDBTable
  • Click the orange Create button.
  1. On the code editor page of the newly created Resolver:
  • Delete the default code and paste the content below into the Configure the request mapping template section:
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },
    "update" : {
        "expression" : "ADD ups :plusOne, version :plusOne",
        "expressionValues" : {
            ":plusOne" : { "N" : 1 }
        }
    }
}
  • Scroll down, delete the default code, and paste the content below into the Configure the response mapping template section:
#if($context.error)
  $util.error($context.error.message, $context.error.type)
#end
$util.toJson($context.result)
  • Click Save in the corner of the page to save

CreateAddPostResolver

  1. Select the Schema tab on the left menu
  • In the Resolvers pane on the right, find the downvotePost field of type Mutation, then select Attach

CreateAddPostResolver

  1. On the Attach resolver configuration page:
  • Resolver type: Select Unit resolver
  • Resolver runtime: Select Velocity Template Language (VTL)
  • Data source name: Select PostDynamoDBTable
  • Click the orange Create button.
  1. On the code editor page of the newly created Resolver:
  • Delete the default code and paste the content below into the Configure the request mapping template section:
{
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },
    "update" : {
        "expression" : "ADD downs :plusOne, version :plusOne",
        "expressionValues" : {
            ":plusOne" : { "N" : 1 }
        }
    }
}
  • Scroll down, delete the default code, and paste the content below into the Configure the response mapping template section:
#if($context.error)
  $util.error($context.error.message, $context.error.type)
#end
$util.toJson($context.result)
  • Click Save in the corner of the page to save

CreateAddPostResolver

  1. Select the Queries tab on the left menu
  • Delete the default code, paste the script below into the Queries section, then click the Run button (the orange Play button)
mutation votePost {
  upvotePost(id:"123") {
    id
    author
    title
    content
    url
    ups
    downs
    version
  }
}
  1. The returned result is similar to the following:
{
  "data": {
    "upvotePost": {
      "id": "123",
      "author": "A new author",
      "title": "An empty story",
      "content": null,
      "url": "https://aws.amazon.com/appsync/",
      "ups": 2,
      "downs": 0,
      "version": 4
    }
  }
}

CreateAddPostResolver

  1. Change the query command to downvotePost
mutation votePost {
  downvotePost(id:"123") {
    id
    author
    title
    content
    url
    ups
    downs
    version
  }
}
  • Click the Run button (the orange Play button)

CreateAddPostResolver

  1. Click the Run button (the orange Play button) again and you will see the “downs” field increment