🏄 AWS CDK 101 - 🐲 GraphQL using AppSync with dynamodb
Tags: typescript, serverless, graphql, aws
Published: 2022-05-30 05:42:44

🔰  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-event-source-mapping-with-cfn-property-override)

🔁 Reposted the previous post at 🔗 [dev to @aravindvcyber](https://dev.to/aravindvcyber/aws-cdk-101-event-source-mapping-with-cfn-property-override-3laa)

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 basic list items and get an item from dynamodb.

## Construction :building_construction:

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


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


## Imports used in this new stack :shinto_shrine:

Here we have imported the below objects to help us in our definition

```ts
import { Duration, Expiration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Table } from 'aws-cdk-lib/aws-dynamodb';
import { GraphqlApi, MappingTemplate, Schema, FieldLogLevel, AuthorizationType } from 'aws-lib-cdk/aws-appsync-alpha';
```

## Appsync construct with the definition :tent:

A new stack is created which is used to define our appsync endpoint as shown below.

Here we will be getting a default API key with an expiration of 7 days so that we can rotate this periodically. Besides that, we have logged our API sufficiently to understand the background process much better.

```ts

export class GqlStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const AppSyncApi = new GraphqlApi(this, 'gqlApi', {
      name: 'gqlApi',
      schema: Schema.fromAsset('assets/messages-schema.gql'),
      xrayEnabled: true,
      logConfig: {
            excludeVerboseContent: false,
            fieldLogLevel: FieldLogLevel.ALL,
      },
      authorizationConfig: {
          defaultAuthorization: {
              authorizationType: AuthorizationType.API_KEY,
              apiKeyConfig: {
                  name: 'default-api-key',
                  description: 'default-api-key-description',
                  expires: Expiration.after(Duration.days(7))
              }
          }
      }
    })

  }
}
```

## Schema definition file :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.

```ts
type Message {
	message: AWSJSON!
}

type MessagesTable {
	createdAt: AWSTimestamp!
	messageId: String!
	event: Message
}

type MessagesTableConnection {
	items: [MessagesTable]
	nextToken: String
    scannedCount: Int
}

type Query {
	getMessage(messageId: String!, createdAt: AWSTimestamp!): MessagesTable
	listMessages(filter: TableMessagesTableFilterInput, limit: Int, nextToken: String): MessagesTableConnection
}

input TableAWSTimestampFilterInput {
	ne: AWSTimestamp
	eq: AWSTimestamp
	le: AWSTimestamp
	lt: AWSTimestamp
	ge: AWSTimestamp
	gt: AWSTimestamp
	contains: AWSTimestamp
	notContains: AWSTimestamp
	between: [AWSTimestamp]
}

input TableMessagesTableFilterInput {
	createdAt: TableAWSTimestampFilterInput
	messageId: TableStringFilterInput
}

input TableStringFilterInput {
	ne: String
	eq: String
	le: String
	lt: String
	ge: String
	gt: String
	contains: String
	notContains: String
	between: [String]
	beginsWith: String
}
```

## Dynamodb connection as a data source :canoe:

Here we are directly integrating dynamodb API with graphql as a data source

```ts
const messages = Table.fromTableName(this,'MessagesTableImport', 'MessagesTable');

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/0tuyrik3qb7ka4h4t9zt.png)
 
![run test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45d17gx5bj4v4c544xuu.png)

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

## Get message resolver :mountain_cableway:

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

```ts
MessagesDS.createResolver({
      typeName: 'Query',
      fieldName: 'getMessage',
      requestMappingTemplate: MappingTemplate.fromFile('assets/getMessageRequest.vtl'),
      responseMappingTemplate: MappingTemplate.fromFile('assets/getMessageResponse.vtl'),
})
```

### getMessageRequest VTL template :star2:

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

### getMessageResponse VTL template :parasol_on_ground:

```js

#set($ctx.result.event = $util.parseJson($ctx.result.event))

$util.toJson($ctx.result)

```

![get message xray](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxv2is74uk0lu4d1taol.png)

## List messages resolver :clown_face:

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

```ts
MessagesDS.createResolver({
        typeName: 'Query',
        fieldName: 'listMessages',
        requestMappingTemplate: MappingTemplate.fromFile('assets/listMessagesRequest.vtl'),
        responseMappingTemplate: MappingTemplate.fromFile('assets/listMessagesResponse.vtl'),
})
```

### listMessagesRequest VTL template :balloon:

```js
{
  "version": "2017-02-28",
  "operation": "Scan",
  "filter": #if($context.args.filter) $util.transform.toDynamoDBFilterExpression($ctx.args.filter) #else null #end,
  "limit": $util.defaultIfNull($ctx.args.limit, 20),
  "nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)),
}
```

### listMessagesResponse VTL template :fishing_pole_and_fish:

```js
#set($children = [])
#foreach($item in $ctx.result.items)
  #set($item.event = $util.parseJson($item.event))
  $util.qr($children.add($item))
#end
#set($ctx.result.items = $children)
$util.toJson($ctx.result)
```

![list message xray](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxdt3cveejyzssvplqol.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.

![appsync explorer get](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nj172y42cqg0k1nipcsz.png)

![appsync explorer list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yinlf4xoojxs5buwkni8.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.

![appollo get ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hsh6w52wunprp4qfqywb.png)

![appollo list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7elg7sv5yd9ci87av95p.png)

## Conclusion :dizzy:

In this exercise, we have tried only the get and list operations to differentiate the effectiveness of the scan and query operation. 

`scannedCount` value in the above results shows the cost associated with list operation as the table gets bigger. I have removed the filter variable to show you what are the scanned records as shown below.

![list view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fjlim7li9w76estct2cm.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

https://dev.to/aravindvcyber/aws-cdk-101-graphql-mutations-using-appsync-with-dynamodb-9n0

🎉 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-using-appsync-with-dynamodb)

🔁 Reposted at 🔗 [dev to @aravindvcyber](https://dev.to/aravindvcyber/aws-cdk-101-event-source-mapping-with-cfn-property-override-3laa)

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🏄 AWS CDK 101 - 🐲 GraphQL using AppSync with dynamodb<br>{ by <a href="https://twitter.com/Aravind_V7?ref_src=twsrc%5Etfw">@Aravind_V7</a> } <br><br>Checkout more such in my page<a href="https://t.co/CuYxnKr0Ig">https://t.co/CuYxnKr0Ig</a><a href="https://t.co/phzOKFlXXO">https://t.co/phzOKFlXXO</a><a href="https://twitter.com/hashtag/aws?src=hash&amp;ref_src=twsrc%5Etfw">#aws</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/dynamodb?src=hash&amp;ref_src=twsrc%5Etfw">#dynamodb</a> <a href="https://twitter.com/hashtag/awscdk?src=hash&amp;ref_src=twsrc%5Etfw">#awscdk</a> <a href="https://t.co/yeqeLZQt8A">https://t.co/yeqeLZQt8A</a></p>&mdash; Aravind V (@Aravind_V7) <a href="https://twitter.com/Aravind_V7/status/1531153165298839552?ref_src=twsrc%5Etfw">May 30, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>