Simple, Serverless CRUD with the CDK
January 04, 2021
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
- Installing Node.js
- Setting up an AWS account
- Actually installing the CDK
- Review the CDK basics.
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
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 you’re 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"