🤹‍♂ AWS CDK 101 - 🏄 GraphQL Mutations using AppSync with dynamodb
Tags: typescript, serverless, aws, graphql
Published: 2022-06-04 18:33:39

🔰  Beginners new to AWS CDK, please do look at my previous articles one by one in this series.

If in case missed my previous article, do find it with the below links.

🔁 Original previous post at 🔗 [Dev Post](https://devpost.hashnode.dev/aws-cdk-101-graphql-using-appsync-with-dynamodb)

🔁 Reposted the previous post at 🔗 [dev to @aravindvcyber](https://dev.to/aravindvcyber/aws-cdk-101-graphql-using-appsync-with-dynamodb-16dd)

In this article, we will be introducing a data access layer as a wrapper on top of our dynamodb table. Here specifically we have chosen graphql using AWS appsync to perform mutations in our dynamodb table.

## Construction :building_construction:

Let us start by updating our previous file `lib/appsync-stack.ts` for our new stack.

![gql front](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1ia7h0q9icmu68kuj0e.png)

## Schema definition for Mutations :roller_coaster:

You may also use the AWS console to update the schema before we update in CDK asset files when we know for sure.

Here you can find the mutations part which we have appended to our schema file which we created in our previous article above.

```ts
input CreateMessagesTableInput {
	createdAt: Int!
	messageId: String!
	event: AWSJSON
}

input UpdateMessagesTableInput {
	createdAt: Int!
	messageId: String!
	event: AWSJSON
}

input DeleteMessagesTableInput {
	createdAt: Int!
	messageId: String!
}

type Mutation {
	createMessagesTable(input: CreateMessagesTableInput!): MessagesTable
	updateMessagesTable(input: UpdateMessagesTableInput!): MessagesTable
	deleteMessagesTable(input: DeleteMessagesTableInput!): MessagesTable
}
```

![schema update](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1xa4frnm6eqegujxkqju.png)

## Dynamodb connection as a data source :canoe:

Here we have already directly integrated dynamodb API with graphql as a data source.

Additionally we are updating our earlier article by making use of the import value of table arn to get the table name directly from the previous stack exports.

### Exporting table arn from another stack :leopard:

```ts
new CfnOutput(this, "MessagesTableExport", {
      value: messages.tableArn,
      exportName: 'MessagesTableArn'
    })
```

### Importing table arn in this stack :cow:

```ts
const messages = Table.fromTableArn(
      this,
      "MessagesTableImport",
      Fn.importValue("MessagesTableArn")
    );

const MessagesDS = AppSyncApi.addDynamoDbDataSource("MessagesDataSource", messages);
```

![GQL visual](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cb6mqco9addahh7q9vgh.png)

## VTL Mapping template :small_airplane:

Here we need to use VTL (Velocity Template Language) to transform/manipulate our request and the response we send/receive from the below resolvers. Using this can be a good strategy as this can be used in many places not only in appsync and API gateway.

You may also use the AWS console to test these transformations using the sample payload from logs before we update in CDK asset files.

![create test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jw5e8d8cmlic00vxxc9.png)

![test result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wu6p2onebbr17pbk62kg.png)


![mutation resolvers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zykwst9bjilqie0kqkv.png)


## Create message resolver :mountain_cableway:

Here you can also look into the Xray trace to understand when these blocks are utilized for `createMessagesTable` resolver

```ts
MessagesDS.createResolver({
      typeName: "Mutation",
      fieldName: "createMessagesTable",
      requestMappingTemplate: MappingTemplate.fromFile(
        "assets/createMessagesTableRequest.vtl"
      ),
      responseMappingTemplate: MappingTemplate.fromFile(
        "assets/createMessagesTableResponse.vtl"
      ),
    });
```

### createMessagesTableRequest VTL template :star2:

```js
{
  "version": "2017-02-28",
  "operation": "PutItem",
  "key": {
    "messageId": $util.dynamodb.toDynamoDBJson($ctx.args.input.messageId),
    "createdAt": $util.dynamodb.toDynamoDBJson($ctx.args.input.createdAt),
  },
  "attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
  "condition": {
    "expression": "attribute_not_exists(#messageId) AND attribute_not_exists(#createdAt)",
    "expressionNames": {
      "#messageId": "messageId",
      "#createdAt": "createdAt",
    },
  },
}
```

### createMessagesTableResponse VTL template :parasol_on_ground:

```js

$util.toJson($context.result)

```

![create xray](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5likrj4x0aposkbcsprr.png)

## Delete message resolver :clown_face:

Here you can also look into the Xray trace to understand when these blocks are utilized for `deleteMessagesTable` resolver

```ts
MessagesDS.createResolver({
      typeName: "Mutation",
      fieldName: "deleteMessagesTable",
      requestMappingTemplate: MappingTemplate.fromFile(
        "assets/deleteMessagesTableRequest.vtl"
      ),
      responseMappingTemplate: MappingTemplate.fromFile(
        "assets/deleteMessagesTableResponse.vtl"
      ),
    });
```

### deleteMessagesTableRequest VTL template :balloon:

```js
{
  "version": "2017-02-28",
  "operation": "DeleteItem",
  "key": {
    "messageId": $util.dynamodb.toDynamoDBJson($ctx.args.input.messageId),
    "createdAt": $util.dynamodb.toDynamoDBJson($ctx.args.input.createdAt),
  },
}
```

### deleteMessagesTableResponse VTL template :fishing_pole_and_fish:

```js
$util.toJson($context.result)
```

![delete xray](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9nvqt9x8d1khkqgeo3d.png)

## Update messages resolver :unicorn:

Here you can also look into the xray trace to understand when these blocks are utilized for `updateMessagesTable` resolver

```ts
MessagesDS.createResolver({
      typeName: "Mutation",
      fieldName: "updateMessagesTable",
      requestMappingTemplate: MappingTemplate.fromFile(
        "assets/updateMessagesTableRequest.vtl"
      ),
      responseMappingTemplate: MappingTemplate.fromFile(
        "assets/updateMessagesTableResponse.vtl"
      ),
    });
```

### updateMessagesTableRequest VTL template :chipmunk:

This update logic may look complex but just remember that while updating we need to take care of updated attributes, removed attributes, and new attributes.

```js
{
  "version": "2017-02-28",
  "operation": "UpdateItem",
  "key": {
    "messageId": $util.dynamodb.toDynamoDBJson($ctx.args.input.messageId),
    "createdAt": $util.dynamodb.toDynamoDBJson($ctx.args.input.createdAt),
  },

  #set( $expNames  = {} )
  #set( $expValues = {} )
  #set( $expSet = {} )
  #set( $expAdd = {} )
  #set( $expRemove = [] )

  ## Looping through each argument, except keys **
  #foreach( $entry in $util.map.copyAndRemoveAllKeys($ctx.args.input, ["messageId", "createdAt"]).entrySet() )
    #if( $util.isNull($entry.value) )
      #set( $discard = ${expRemove.add("#${entry.key}")} )
      $!{expNames.put("#${entry.key}", "${entry.key}")}
    #else
      $!{expSet.put("#${entry.key}", ":${entry.key}")}
      $!{expNames.put("#${entry.key}", "${entry.key}")}
      $!{expValues.put(":${entry.key}", $util.dynamodb.toDynamoDB($entry.value))}
    #end
  #end

## Updating existing attributes **
  #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

  ## Adding new attributes **
  #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

  ## Removing unwanted attributes **
  #if( !${expRemove.isEmpty()} )
    #set( $expression = "${expression} REMOVE" )

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

  ## Final update expression **
  "update": {
    "expression": "${expression}",
    #if( !${expNames.isEmpty()} )
      "expressionNames": $utils.toJson($expNames),
    #end
    #if( !${expValues.isEmpty()} )
      "expressionValues": $utils.toJson($expValues),
    #end
  },

  "condition": {
    "expression": "attribute_exists(#messageId) AND attribute_exists(#createdAt)",
    "expressionNames": {
      "#messageId": "messageId",
      "#createdAt": "createdAt",
    },
  }
}
```

### updateMessagesTableResponse VTL template :mouse2:

```js
$util.toJson($context.result)
```

![update xray](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d40ewxttepcqw6l5210u.png)

## Client to explore graphQl :snowflake:

![client](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cqlmtxa3vub8xqx6lxb.png)

### Appsync Explorer Queries :hotsprings:

In the AWS console, you can navigate the appsync and start querying. One advantage you have here is that we have cloud watch and tracing logs readily available if in case you want to check.

![create](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hrvd4cy8yfsqdn7k2oyt.png)

![update](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxwbsvjxjcrwloffhgag.png)

![delete](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n41ywnnazwaug4inpqbd.png)

### Apollo Studio Queries :camping:

But normally prefer the dark mode in apollo graphql studio, you may also try it out if you prefer that, maybe we would get that in the AWS console as well someday.

![collection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/85bvtu1pim9rmvbbtsub.png)
 

![create apollo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4pk5qf2w2rp12jx7i2p.png)

![update apollo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ooq1no01boaehmgefmex.png)

![del apollo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6x00wrf9bba30go03wzi.png)

We will be refining this in our coming articles to achieve optimum speed and resource utilization.

We will be adding more connections to our stack and making it more usable in the upcoming articles by creating new constructs, so do consider following and subscribing to my newsletter.

⏭ We have our next article in serverless, do check out

🎉 Thanks for supporting! 🙏

Would be great if you like to [☕ Buy Me a Coffee](https://www.buymeacoffee.com/AravindVCyber), to help boost my efforts 😍.

<a href="https://www.buymeacoffee.com/AravindVCyber"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=AravindVCyber&button_colour=BD5FFF&font_colour=ffffff&font_family=Cookie&outline_colour=000000&coffee_colour=FFDD00" /></a>

<a href='https://ko-fi.com/X8X0CITDJ' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi4.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>

🔁 Original post at 🔗 [Dev Post](https://devpost.hashnode.dev/aws-cdk-101-graphql-mutations-using-appsync-with-dynamodb)

🔁 Reposted at 🔗 [dev to @aravindvcyber](https://dev.to/aravindvcyber/aws-cdk-101-graphql-mutations-using-appsync-with-dynamodb-9n0)

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🤹‍♂ AWS CDK 101 - 🏄 GraphQL Mutations using AppSync with dynamodb<br><br>Checkout more in my page<a href="https://t.co/CuYxnKr0Ig">https://t.co/CuYxnKr0Ig</a><a href="https://twitter.com/hashtag/Serverless?src=hash&amp;ref_src=twsrc%5Etfw">#Serverless</a><a href="https://twitter.com/hashtag/typescript?src=hash&amp;ref_src=twsrc%5Etfw">#typescript</a> <a href="https://twitter.com/hashtag/graphql?src=hash&amp;ref_src=twsrc%5Etfw">#graphql</a> <a href="https://twitter.com/hashtag/awscdk?src=hash&amp;ref_src=twsrc%5Etfw">#awscdk</a> <a href="https://twitter.com/hashtag/dynamodb?src=hash&amp;ref_src=twsrc%5Etfw">#dynamodb</a> <a href="https://twitter.com/hashtag/aws?src=hash&amp;ref_src=twsrc%5Etfw">#aws</a> <a href="https://t.co/3y1zUMIyFk">https://t.co/3y1zUMIyFk</a></p>&mdash; Aravind V (@Aravind_V7) <a href="https://twitter.com/Aravind_V7/status/1533157732987342848?ref_src=twsrc%5Etfw">June 4, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>