๐ฐ 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 mutations in our dynamodb table.
Construction ๐๏ธ
Let us start by updating our previous file lib/appsync-stack.ts
for our new stack.
Schema definition for Mutations ๐ข
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.
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
}
Dynamodb connection as a data source ๐ถ
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 ๐
new CfnOutput(this, "MessagesTableExport", {
value: messages.tableArn,
exportName: 'MessagesTableArn'
})
Importing table arn in this stack ๐ฎ
const messages = Table.fromTableArn(
this,
"MessagesTableImport",
Fn.importValue("MessagesTableArn")
);
const MessagesDS = AppSyncApi.addDynamoDbDataSource("MessagesDataSource", messages);
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 message resolver ๐
Here you can also look into the Xray trace to understand when these blocks are utilized for createMessagesTable
resolver
MessagesDS.createResolver({
typeName: "Mutation",
fieldName: "createMessagesTable",
requestMappingTemplate: MappingTemplate.fromFile(
"assets/createMessagesTableRequest.vtl"
),
responseMappingTemplate: MappingTemplate.fromFile(
"assets/createMessagesTableResponse.vtl"
),
});
createMessagesTableRequest VTL template ๐
{
"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 โฑ๏ธ
$util.toJson($context.result)
Delete message resolver ๐คก
Here you can also look into the Xray trace to understand when these blocks are utilized for deleteMessagesTable
resolver
MessagesDS.createResolver({
typeName: "Mutation",
fieldName: "deleteMessagesTable",
requestMappingTemplate: MappingTemplate.fromFile(
"assets/deleteMessagesTableRequest.vtl"
),
responseMappingTemplate: MappingTemplate.fromFile(
"assets/deleteMessagesTableResponse.vtl"
),
});
deleteMessagesTableRequest VTL template ๐
{
"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 ๐ฃ
$util.toJson($context.result)
Update messages resolver ๐ฆ
Here you can also look into the xray trace to understand when these blocks are utilized for updateMessagesTable
resolver
MessagesDS.createResolver({
typeName: "Mutation",
fieldName: "updateMessagesTable",
requestMappingTemplate: MappingTemplate.fromFile(
"assets/updateMessagesTableRequest.vtl"
),
responseMappingTemplate: MappingTemplate.fromFile(
"assets/updateMessagesTableResponse.vtl"
),
});
updateMessagesTableRequest VTL template ๐ฟ๏ธ
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.
{
"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 ๐
$util.toJson($context.result)
Client to explore graphQl โ๏ธ
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.
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.
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 ๐.
๐ Original post at ๐ Dev Post
๐ Reposted at ๐ dev to @aravindvcyber
๐คนโโ AWS CDK 101 - ๐ GraphQL Mutations using AppSync with dynamodb
โ Aravind V (@Aravind_V7) June 4, 2022
Checkout more in my pagehttps://t.co/CuYxnKr0Ig#Serverless#typescript #graphql #awscdk #dynamodb #aws https://t.co/3y1zUMIyFk
Top comments (0)