Getting into Serverless with CDK, Lambda & DynamoDB - Part 2
In this second post, we are going to add a DynamoDB Table to our stack for our lambda function to pull data from. See Part 1 on getting started to get caught up.
Before we go too far, you are going to need:
- Python 3.8 with Pip installed
- AWS SAM CLI
Step 1 - Defining the table.
Install the DynamoDB CDK module, being careful to use the same release you used before.
npm install @aws-cdk/aws-dynamodb@1.33.1
And add it to our imports in lib/myapp-stack.ts
.
import * as dynamodb from "@aws-cdk/aws-dynamodb";
Next, we can add a DynamoDB Table construct to our stack. DynamoDB is a no-sql database and requires you to think a little bit about how your data will be accessed. Read about the key concepts over here on dynamodbguide.com
const tourfinishersTable = new dynamodb.Table(this, "TourFinishersTable", {
partitionKey: { name: "Year", type: dynamodb.AttributeType.NUMBER },
sortKey: { name: "Position", type: dynamodb.AttributeType.NUMBER },
tableName: "TourFinishers",
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
We have chosen in this instance to make the Year
the primary key and Position
the sort key, together this will be a unique value within this simple context a high cardinality. It will support our access pattern later as will.
Next step is to grant our function read access to the table.
tourfinishersTable.grantReadData(myFunction);
I think this is one of the most powerful features of CDK, it makes dealing with permissions so easy that you don’t feel the need to grant overly permissive policies.
Step 2 - Updating out function
Our python function is going to be a little more complex than before and will require some dependencies. Therefore we first need to move it into its own src
directory.
mkdir function/src
mv function/index.py function/src/
Next, we will want to add a requreiments.txt file at function/src/requirements.txt
and add the following dependencies.
boto3>=1.12.43
Now we will write a build.sh
script at the base of our project to build are code bundle. AWS Lambda’s require a code bundle to come pre-packaged with all its dependencies.
#!/bin/bash
mkdir -p function/_bundle
pip3.8 install -r function/src/requirements.txt --target function/_bundle
cp -r function/src/* function/_bundle
pushd function/_bundle
zip -r --quiet ../bundle.zip .
popd
rm -rf function/_bundle
And now we can update our code function/src/index.py
to pull the data from dynamoDB.
import boto3
from boto3.dynamodb.conditions import Key
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('TourFinishers')
def main(event, context):
logger.info("Start of function")
response = table.query(
KeyConditionExpression=Key('Year').eq(2019)
)
return {
"statusCode": 200,
"body": response["Items"],
"headers": {
"Content-Type": "application/json"
}
}
Next, we run ./build.sh
to build are bundle and then we can point CDK at our code bundle by updating the code
property of our lambda in lib/myapp-stack.ts
.
code: lambda.Code.fromAsset(
path.join(__dirname, "../function/bundle.zip")
),
Step 3 - Deploy the changes
Simply build and deploy are changes to AWS.
npm run build
cdk deploy
To speed things along for this demo, I have a gist that has the top 3 winners of the last 3 Tours that we can import into our table in AWS directly.
aws dynamodb batch-write-item --request-items https://gist.githubusercontent.com/almcc/1437d07e5f344bb8a5b03376a9473af2/raw/a96c545774c5a046b8e7b32e597ded0f7a84306c/TourFinishers.json
Step 4 - Execute to lambda
Now to run your function in AWS we can use the AWS command.
aws lambda invoke --function-name MyappStack-MyFunction3BAA72D1-1H78B5LRDK3NI >(cat | jq) --log-type Tail | jq --raw-output .LogResult | base64 -d
You should see something a bit like this as the output.
{
"statusCode": 200,
"body": [
{
"RiderNumber": 2,
"Position": 1,
"FirstName": "Egan",
"LastName": "Bernal",
"TeamName": "Team INEOS",
"Year": 2019
},
{
"RiderNumber": 1,
"Position": 2,
"FirstName": "Geraint",
"LastName": "Thomas",
"TeamName": "Team INEOS",
"Year": 2019
},
{
"RiderNumber": 81,
"Position": 3,
"FirstName": "Steven",
"LastName": "Kruijswijk",
"TeamName": "Team Jumbo-Visma",
"Year": 2019
}
],
"headers": {
"Content-Type": "application/json"
}
}
...
Bonus Step - Running the function locally
You can also run the function locally with the AWS sam CLI which can be handy during development.
The first thing to do is to get hold of the CloudFormation template that CDK is creating behind the scenes for us.
cdk synth --no-staging > template.yaml
Now you can use the sam
CLI to invoke your function. (Check the contents of template.yaml
for the functions name)
sam local invoke MyFunction3BAA72D1 --no-event | jq
This will run your code locally, using your local credentials to query the live table in AWS.
Then if you make any changes to you code you will need to run ./build.sh
again, if you make a change to your template, you will need to do the following.
npm run build
cdk synth --no-staging > template.yaml
Job done!
That’s it for this post, Next time we will look at adding the API gateway.
If you would like to clean up everything you have created in AWS, see Step 6 - Cleaning up from the first post in this series.