DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Exploring AWS CDK, AWS AppSync, and Amazon DynamoDB
Bervianto Leo Pratama for AWS Community Builders

Posted on • Updated on

Exploring AWS CDK, AWS AppSync, and Amazon DynamoDB

Introduction

Hello everyone! I want to share my experience of exploring AWS CDK. I want to deploy AWS AppSync and Amazon DynamoDB. Let's learn with me.

Background

I used Terraform for provisioning the resources into Heroku. Heroku announced that they will stop the free plan, so I want to move my resources into AWS. I learned AWS SDK 3 months ago, but got stuck. I hope this post will help you.

Preparation

  1. Install Node.js
  2. Install AWS CDK Toolkit. Command: npm install -g aws-cdk. More informations.
  3. If you want to generate a new project. You can use cdk init app --language csharp. More informations.

Update the Stack

I want simple architecture. You can see my architecture below.

arch

  • I have GraphQL schema like this:
type Experience {
  id: ID!
  title: String!
}

input ExperienceInput {
  title: String!
}

input UpdateExperienceInput {
  id: ID!
  title: String
}

type Query {
  getExperienceById(experienceId: String!): Experience
  getExperiences: [Experience]
}

type Mutation {
  addExperience(input: ExperienceInput!): Experience
  updateExperience(input: UpdateExperienceInput!): Experience
  deleteExperience(experienceId: String!): String
}
Enter fullscreen mode Exit fullscreen mode

Our schema only has simple CRUD and store experience data. The experience data has id and title only. You can extend more columns on it, but take note you will need to update the CDK code too.

  • My CDK stacks look like this:
using Amazon.CDK;
using Amazon.CDK.AWS.AppSync.Alpha;
using Amazon.CDK.AWS.DynamoDB;
using Amazon.CDK.AWS.IAM;
using Constructs;

namespace MyPersonalWebApiInfra
{
    public class MyPersonalWebApiInfraStack : Stack
    {
        internal MyPersonalWebApiInfraStack(Construct scope, string id, IStackProps props = null) : base(scope, id,
            props)
        {
            // AppSync
            var api = new GraphqlApi(this, "api", new GraphqlApiProps
            {
                Name = "berv-api",
                Schema = Schema.FromAsset("schema.graphql"),
                XrayEnabled = true,
            });

            // Dynamo DB
            var experienceTable = new Table(this, "berv-experiences", new TableProps
            {
                BillingMode = BillingMode.PAY_PER_REQUEST,
                PartitionKey = new Attribute
                {
                    Name = "id",
                    Type = AttributeType.STRING
                },
            });

            // IAM Role
            var role = new Role(this, "Role", new RoleProps
            {
                AssumedBy = new ServicePrincipal("lambda.amazonaws.com")
            });

            // AppSync apply IAM to some mutations
            api.GrantMutation(role, "addExperience");
            api.GrantMutation(role, "updateExperience");
            api.GrantMutation(role, "deleteExperience");

            // Add Dynamo DB as DataSource of AppSync
            var dataSource = api.AddDynamoDbDataSource("experience", experienceTable);

            // Add Resolver for Get All Experiences
            dataSource.CreateResolver(new BaseResolverProps
            {
                TypeName = "Query",
                FieldName = "getExperiences",
                RequestMappingTemplate = MappingTemplate.DynamoDbScanTable(),
                ResponseMappingTemplate = MappingTemplate.DynamoDbResultList(),
            });

            // Add Resolver for Get Experience by Id
            dataSource.CreateResolver(new BaseResolverProps
            {
                TypeName = "Query",
                FieldName = "getExperienceById",
                RequestMappingTemplate = MappingTemplate.DynamoDbGetItem("id", "experienceId"),
                ResponseMappingTemplate = MappingTemplate.DynamoDbResultItem(),
            });

            // Add Resolver for Create Experience
            dataSource.CreateResolver(new BaseResolverProps
            {
                TypeName = "Mutation",
                FieldName = "addExperience",
                RequestMappingTemplate =
                    MappingTemplate.DynamoDbPutItem(PrimaryKey.Partition("id").Auto(), Values.Projecting("input")),
                ResponseMappingTemplate = MappingTemplate.DynamoDbResultItem()
            });

            // Add Resolver for Update an Experience
            dataSource.CreateResolver(new BaseResolverProps
            {
                TypeName = "Mutation",
                FieldName = "updateExperience",
                RequestMappingTemplate = MappingTemplate.FromString("""
                { 
                    "version" : "2017-02-28",
                    "operation" : "PutItem",
                    "key": {
                        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
                    },
                    "attributeValues" : {
                        "title" : $util.dynamodb.toDynamoDBJson($ctx.args.input.title)
                    }
                }
                """),
                ResponseMappingTemplate = MappingTemplate.DynamoDbResultItem()
            });

            // Add Resolver for Delete an Experience
            dataSource.CreateResolver(new BaseResolverProps
            {
                TypeName = "Mutation",
                FieldName = "deleteExperience",
                RequestMappingTemplate = MappingTemplate.DynamoDbDeleteItem("id", "experienceId"),
                ResponseMappingTemplate = MappingTemplate.FromString("$util.toJson($ctx.result)")
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

I didn't have enough information on the best way to map the Update Request. Please feel free to give feedback about it. Anyway, visit this repository for the complete project.

GitHub logo bervProject / my-personal-web-api-infra

Infrastructure as Code for My Personal Web API

Welcome to your CDK C# project!

This is a blank project for CDK development with C#.

The cdk.json file tells the CDK Toolkit how to execute your app.

It uses the .NET Core CLI to compile and execute your project.

Useful commands

  • dotnet build src compile this app
  • cdk deploy deploy this stack to your default AWS account/region
  • cdk diff compare deployed stack with current state
  • cdk synth emits the synthesized CloudFormation template

Deploying and Testing

  • You can deploy the app using cdk deploy. Please make sure you've set up the account in your environment. If you're not sure, please check here.
  • Let's test our AppSync using in-browser tools provided by AppSync. You can click Run a query button.

Run a query

  • Test Create

Create

  • Test Update

Update

  • Test Get Experiences

Get Experiences

  • Test Get Experience by Id

Get Experience by Id

  • Test Delete

Delete

Thank you

Thanks GIF

Top comments (0)

Timeless DEV post...

Git Concepts I Wish I Knew Years Ago

The most used technology by developers is not Javascript.

It's not Python or HTML.

It hardly even gets mentioned in interviews or listed as a pre-requisite for jobs.

I'm talking about Git and version control of course.

One does not simply learn git