Aravind V
Dev Post

Dev Post

๐Ÿ„ AWS CDK 101 - ๐Ÿฒ GraphQL using AppSync with dynamodb

๐Ÿ„ AWS CDK 101 - ๐Ÿฒ GraphQL using AppSync with dynamodb

Aravind V's photo
Aravind V
ยทMay 30, 2022ยท

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

๐Ÿ”ฐ 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

๐Ÿ” Reposted the previous post at ๐Ÿ”— dev to @aravindvcyber

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 ๐Ÿ—

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

gql front

Imports used in this new stack โ›ฉ

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

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 definition โ›บ๏ธ

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.


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 ๐ŸŽข

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

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 ๐Ÿ›ถ

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

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

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

GQL visual

VTL Mapping template ๐Ÿ›ฉ

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

run test

resolvers

Get message resolver ๐Ÿš 

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

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

getMessageRequest VTL template ๐ŸŒŸ

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

getMessageResponse VTL template โ›ฑ


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

$util.toJson($ctx.result)

get message xray

List messages resolver ๐Ÿคก

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

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

listMessagesRequest VTL template ๐ŸŽˆ

{
  "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 ๐ŸŽฃ

#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

Client to explore graphQl โ„๏ธ

client

Appsync Explorer Queries โ™จ๏ธ

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

appsync explorer list

Apollo Studio Queries ๐Ÿ•

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

appollo list

Conclusion ๐Ÿ’ซ

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

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, to help boost my efforts ๐Ÿ˜.

Buy Me a Coffee at ko-fi.com

๐Ÿ” Original post at ๐Ÿ”— Dev Post

๐Ÿ” Reposted at ๐Ÿ”— dev to @aravindvcyber

๐Ÿ„ AWS CDK 101 - ๐Ÿฒ GraphQL using AppSync with dynamodb
{ by @Aravind_V7 }

Checkout more such in my pagehttps://t.co/CuYxnKr0Ighttps://t.co/phzOKFlXXO#aws #typescript #graphql #dynamodb #awscdk https://t.co/yeqeLZQt8A

โ€” Aravind V (@Aravind_V7) May 30, 2022

Did you find this article valuable?

Support Aravind V by becoming a sponsor. Any amount is appreciated!

See recent sponsors |ย Learn more about Hashnode Sponsors
ย 
Share this

Impressum

Also you can find my dev.to profile at โœจ @aravindvcyber for similar posts and contributions to community.

Are you wondering about reading more posts like this or writing your own post with hashnode.

Hashnode is free to ๐Ÿ”— sign-up, using my link helps to enable more features in my blog.

Would you like to โ˜• Buy Me a Coffee to help boost my efforts.

Also you can follow me on twitter, to find my latest articles and shares ๐Ÿ“ฃ @Aravind_V7