Explore previous articles in this series by clicking here.

Creating Movie Lists with AppSync GraphQL and VTL(Part -1)

Introduction:

AppSync is a powerful service provided by AWS that allows you to build scalable and real-time applications with GraphQL. In this article, we’ll explore how to use AppSync and VTL (Velocity Template Language) to update movie lists in a serverless environment.

Understanding the GraphQL Schema:

The first step is to understand the GraphQL schema that describes the structure of our movie lists. The schema includes the updateMovieList mutation and the input type updateMovieListInput, which contains the fields to be updated in the movie list.

updateMovieList(input: updateMovieListInput): statusItems
input updateMovieListInput {
	PK: String!
	SK: String!
	name: String
	description: String
}
type statusItems {
	statusCode: String
	statusMessage: String
}

Schema Explanation:

Sure, I’ll explain the AppsSync GraphQL schema in detail for the given types and input.

1. `updateMovieList` Mutation:

   This mutation is used to update a movie list with new information. It takes an `updateMovieListInput` as an argument and returns a `statusItems` type indicating the status of the update operation.

2. `updateMovieListInput` Input Type:

   This is an input type used as an argument for the `updateMovieList` mutation. It contains the following fields:

   -> `PK` (String!): The primary key for identifying the movie list. It is a mandatory field (indicated by the `!`), meaning it must be provided when calling the mutation. The primary key uniquely identifies the movie list.

   -> `SK` (String!): The sort key for identifying the movie list. It is also a mandatory field (indicated by the `!`). The sort key works together with the primary key to identify a specific item.

   -> `name` (String): The new name for the movie list. It is an optional field, indicated by not having the `!` symbol. If provided, the movie list’s name will be updated with this new value.

   -> `description` (String): The new description for the movie list. Like the `name` field, it is optional and can be updated if a new description is provided.

3. `statusItems` Type:

   This is the return type for the `updateMovieList` mutation. It contains the following fields:

   -> `statusCode` (String): The status code indicating the result of the update operation. It can be any string value and is used to represent the status of the mutation. For example, “SUCCESS” or “ERROR.”

   -> `statusMessage` (String): A descriptive message associated with the status code. It provides additional information about the result of the mutation. For example, “Movie list updated successfully” or “Failed to update movie list.”

In summary, the given AppsSync GraphQL schema defines a mutation `updateMovieList`, which takes an input object `updateMovieListInput` containing information to update a movie list. The mutation returns a `statusItems` object with status information about the update operation.

When calling the `updateMovieList` mutation, you must provide the mandatory fields `PK` and `SK`, while the `name` and `description` fields are optional. The response will contain the status code and message to indicate the result of the mutation.

Request Mapping Template:

The request mapping template is responsible for transforming the incoming GraphQL request into a format that can be understood by the underlying data source, in this case, DynamoDB. In the provided VTL code, we first set the current time using the $util.time.nowEpochSeconds() function. Then, we iterate through each argument in the input, excluding the primary key fields (PK and SK). For each argument, we check if it’s set to null and remove the corresponding attribute from the DynamoDB item. If the argument has a value, we update the attribute on the item. We also build the update expression based on the attributes to be set, added, or removed. Finally, we define a condition expression to ensure that the primary key attributes exist in the item.

## Below example shows how to create an object from all provided GraphQL arguments
#set( $time = $util.time.nowEpochSeconds() )
$util.qr($ctx.args.input.put("updatedTime",$time))
## $util.qr($ctx.args.input.put("movieListTypeAndCategory","$ctx.args.input.movieListType#$ctx.args.input.movieListCategory"))

{
"version": "2017-02-28",
"operation": "UpdateItem",
"key": {
"PK": $util.dynamodb.toDynamoDBJson($ctx.args.input.PK),
"SK": $util.dynamodb.toDynamoDBJson($ctx.args.input.SK),
},

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

## Iterate through each argument, skipping keys **
#foreach( $entry in $util.map.copyAndRemoveAllKeys($ctx.args.input, ["PK", "SK"]).entrySet() )
#if( $util.isNull($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}", $util.dynamodb.toDynamoDB($entry.value))}
#end
#end

## Start building the update expression, starting with attributes we’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 we’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 we’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": "attribute_exists(#PK) AND attribute_exists(#SK)",
"expressionNames": {
"#PK": "PK",
"#SK": "SK",
},
}
}

Response Mapping Template:

The response mapping template handles the response from the data source and transforms it into the desired GraphQL response format. If an error occurs during the data operation, the VTL code checks for the presence of an error and appends it to the GraphQL field error. Otherwise, it constructs the status object with the desired status code and message using the put function. Finally, it converts the status object to JSON using $util.toJson() and returns it as the GraphQL response.

## Raise a GraphQL field error in case of a datasource invocation error

#set($status = {})
#if($context.error)
$utils.appendError($ctx.error.message, $ctx.error.message)
#else
$util.qr($status.put("statusCode", "M201"))
$util.qr($status.put("statusMessage", "Updated Movie List Successfully"))
#end
$util.toJson($status)

Conclusion:

Here, we explored the process of updating movie lists using AppSync GraphQL and VTL. We discussed the GraphQL schema, request mapping template, and response mapping template used in the AppSync resolver. By understanding these components, you can leverage the power of AppSync and VTL to perform updates on your movie lists in a serverless environment.

By following the steps outlined in this article, you can implement the given functionality of updating movie lists in your own AppSync GraphQL service. Remember to adapt the code to fit your specific requirements and consider additional features such as input validation, authorization, and error handling.

Explore Next Articles in this series by clicking here.

Deleting Movie Lists with AppSync GraphQL and VTL(Part -3)

Fetching Movie Lists with AppSync GraphQL and AppSync JavaScript(Part -4)