Simple, Serverless CRUD with the CDK

This post provides some basic information on the CDK as well as some code examples to get you started.

Setup

The first step is to get the CDK installed. This requires

CDK CLI

The two main commands for the CDK cli are cdk synth and cdk deploy. synth will output a template. deploy will generate the template and deploy it.

Base Template

The AWS docs do a pretty good job of explaining how to get off the ground. The focus of this guide is providing a backend for simple CRUD operations.

The amplify-base-cdk.py describes a base level stack for supporting CRUD operations. Comments are included to explain what's going on.

Why the CDK?

There are a few reasons why the CDK is awesome!

You get to work in a language you're familiar with

YAML/JSON can be challenging to work with. There are tools like cfn-lint, but it can only go so far in linting a YAML file.

Using a language of your choice makes life easier for you and you get extensive type checking, linting, and validation. synth and deploy ensure that you have a valid template.

Declarative with use of programming language features

The CDK is highly declarative. Related to higher level constructs, rather than having to explicitly define a policy and attach it to a role, you can do something like

python table.grant_read_write_data(function)

The CDK defines all sorts of interfaces to make this possible. All that's required is the argument passed to grant_read_write_table adheres to an interface that exposes a IAM principal.

You can also make use of programming language features such as if/else and looping. If you know all of your Lambda functions will use the Python 3.8 runtime, have the same handler function definition, and make use of a table you could create

      
def create_python_function(function_name, function_path):
    parent_path = Path(__file__).parent.absolute()
    return  function = lambda_.Function(self, function_name,
                code=lambda_.Code.from_asset(str(Path(parent_path, function_path))),
                runtime=lambda_.Runtime.PYTHON_3_8,
                handler='handler.handler',
                environment={'TABLE': table.table_name})
    
    

Bye bye boilerplate!

Higher Level Constructs

The CDK represents CloudFormation resources as constructs. Raw CloudFormation is quite verbose. For the resources that make use of higher level constructs, things are quieted down. For example in the CDK a table may look like

      
table = dynamodb.Table(self, 'Table',partition_key=dynamodb.Attribute(name='pk', type=dynamodb.AttributeType.STRING), sort_key=dynamodb.Attribute(name='sk', type=dynamodb.AttributeType.STRING))
table.add_global_secondary_index(partition_key=dynamodb.Attribute(name='sk', type=dynamodb.AttributeType.STRING), sort_key=dynamodb.Attribute(name='gsi1sk', type=dynamodb.AttributeType.STRING), index_name='gsi1')
        
      

and a corresponding YAML file may look like


myDynamoDBTable: 
  Type: AWS::DynamoDB::Table
  Properties: 
    AttributeDefinitions: 
      - 
        AttributeName: "Album"
        AttributeType: "S"
      - 
        AttributeName: "Artist"
        AttributeType: "S"
      - 
        AttributeName: "Sales"
        AttributeType: "N"
      - 
        AttributeName: "NumberOfSongs"
        AttributeType: "N"
    KeySchema: 
      - 
        AttributeName: "Album"
        KeyType: "HASH"
      - 
        AttributeName: "Artist"
        KeyType: "RANGE"
    ProvisionedThroughput: 
      ReadCapacityUnits: "5"
      WriteCapacityUnits: "5"
    TableName: "myTableName"
    GlobalSecondaryIndexes: 
      - 
        IndexName: "myGSI"
        KeySchema: 
          - 
            AttributeName: "Sales"
            KeyType: "HASH"
          - 
            AttributeName: "Artist"
            KeyType: "RANGE"
        Projection: 
          NonKeyAttributes: 
            - "Album"
            - "NumberOfSongs"
          ProjectionType: "INCLUDE"
        ProvisionedThroughput: 
          ReadCapacityUnits: "5"
          WriteCapacityUnits: "5"