Last updated: Jan 26, 2024
Reading timeยท6 min
In order to create a DynamoDB table in AWS CDK, we have to instantiate the Table class.
Let's look at a simple example where we create a DynamoDB table and add a local secondary index to it.
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ๐ create Dynamodb table const table = new dynamodb.Table(this, id, { billingMode: dynamodb.BillingMode.PROVISIONED, readCapacity: 1, writeCapacity: 1, removalPolicy: cdk.RemovalPolicy.DESTROY, partitionKey: {name: 'id', type: dynamodb.AttributeType.STRING}, sortKey: {name: 'createdAt', type: dynamodb.AttributeType.NUMBER}, pointInTimeRecovery: true, }); console.log('table name ๐', table.tableName); console.log('table arn ๐', table.tableArn); // ๐ add local secondary index table.addLocalSecondaryIndex({ indexName: 'statusIndex', sortKey: {name: 'status', type: dynamodb.AttributeType.STRING}, projectionType: dynamodb.ProjectionType.ALL, }); } }
Let's go over what we did in the code sample.
Table
class are:billingMode
- we set it as PROVISIONED
. The alternative is
PAY_PER_REQUEST
, where the table will automatically scale with the traffic.
readCapacity
and writeCapacity
- the provisioned throughput for the table
removalPolicy
- specify what should happen to the table if we delete the CDK
stack or the table resource itself.
The default removalPolicy
for stateful resources (databases,
S3 buckets,
Cognito User Pools, etc) is
RETAIN
, which means that the resource will remain in an orphaned state in
the account, even after the CDK stack is deleted.
partitionKey
and sortKey
- the primary key for our Dynamodb table
pointInTimeRecovery
- when set to true
- enables continuous backups for
our Dynamodb table
Let's issue the deployment command.
npx aws-cdk deploy
If we take a look at the DynamoDB management console, we can see that the table has been created.
In order to grant permissions on a Dynamodb table in CDK, we have to use the
grant*
methods on an instance of the
Table
class, for example:
Let's look at an example of using grantReadData
:
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ... rest // ๐ grant permissions on table table.grantReadData(new iam.AccountRootPrincipal()); } }
We allowed the AWS account, into which the stack was deployed, to execute read operations on the DynamoDB table.
A very common scenario is to use the grant*
methods to grant permissions to a
Lambda function. For example, this line
grants permission to a lambda to execute all read operations on the table:
table.grantReadData(lambda);
A more fine-grained approach would be to use the grant
method and specify the
action:
table.grant(lambda, ['dynamodb:Query']);
By using the grant
method, we can follow the IAM best practice of granting
only the permissions that are required to perform a task.
In order to configure auto-scaling for a DynamoDB table in CDK, we have to use the autoScaleReadCapacity and autoScaleWriteCapacity methods on an instance of the Table class.
Let's look at an example where we configure auto-scaling for the write capacity of our table.
import * as appautoscaling from 'aws-cdk-lib/aws-applicationautoscaling'; import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ... rest // ๐ configure auto scaling on table const writeAutoScaling = table.autoScaleWriteCapacity({ minCapacity: 1, maxCapacity: 2, }); // ๐ scale up when write capacity hits 75% writeAutoScaling.scaleOnUtilization({ targetUtilizationPercent: 75, }); // ๐ scale up at 9 o'clock in the morning writeAutoScaling.scaleOnSchedule('scale-up', { schedule: appautoscaling.Schedule.cron({hour: '9', minute: '0'}), minCapacity: 2, }); // ๐ scale down in the afternoon writeAutoScaling.scaleOnSchedule('scale-down', { schedule: appautoscaling.Schedule.cron({hour: '14', minute: '0'}), maxCapacity: 2, }); } }
In the code sample:
Let's deploy the changes.
npx aws-cdk deploy
If we take a look at the Dynamodb management console, we can see that the write capacity auto-scaling configuration of the table has been updated:
To delete the resources we've provisioned, issue the destroy
command:
npx aws-cdk destroy
In order to delete a Dynamodb table on CDK destroy we need to override the
table's removalPolicy
property and set it to DESTROY
.
const table = new dynamodb.Table(this, 'my-table', { // ๐ set RemovalPolicy to DESTROY removalPolicy: cdk.RemovalPolicy.DESTROY, partitionKey: {name: 'date', type: dynamodb.AttributeType.STRING}, });
If we were to run cdk destroy
after we've updated the table's removalPolicy
,
our table would get deleted along with the stack.
By default when we provision a Dynamodb table using AWS CDK its removalPolicy
property is set to RETAIN
-
docs.
const table = new dynamodb.Table(this, 'my-table', { removalPolicy: cdk.RemovalPolicy.RETAIN, partitionKey: {name: 'date', type: dynamodb.AttributeType.STRING}, });
The same counts for any stateful resources we provision, i.e. s3
buckets.
The default behavior is that the Dynamodb table will not get deleted, instead it will remain in our account as an orphaned resource from the stack.
The same counts in the case that we delete the Dynamodb table resource from the stack, it will still remain in our account.
To add a Global Secondary Index to a Dynamodb Table in AWS CDK, we have to use the addGlobalSecondaryIndex method on an instance of the Table class.
Let's look at a simple example where we create a DynamoDB table and add a global secondary index to it.
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as cdk from 'aws-cdk-lib'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // ๐ create Dynamodb table const table = new dynamodb.Table(this, id, { partitionKey: {name: 'todoId', type: dynamodb.AttributeType.STRING}, sortKey: {name: 'createdAt', type: dynamodb.AttributeType.NUMBER}, billingMode: dynamodb.BillingMode.PROVISIONED, readCapacity: 1, writeCapacity: 1, removalPolicy: cdk.RemovalPolicy.DESTROY, }); // ๐ add global secondary index table.addGlobalSecondaryIndex({ indexName: 'userIdIndex', partitionKey: {name: 'userId', type: dynamodb.AttributeType.STRING}, sortKey: {name: 'status', type: dynamodb.AttributeType.STRING}, readCapacity: 1, writeCapacity: 1, projectionType: dynamodb.ProjectionType.ALL, }); } }
Let's go over the code snippet.
addGlobalSecondaryIndex
method. The props we have passed to the method are:indexName
- the name of the global secondary indexpartitionKey
- the partition key attribute for the global secondary indexsortKey
- the sort key attribute for the global secondary indexreadCapacity
and writeCapacity
- the capacity for the global secondary
index. These properties can only be provided if the table is configured with
PROVISIONED
capacity. By default readCapacity
and writeCapacity
are set
to 5
.projectionType
- which attributes should be projected into the global
secondary index. By default all attributes are projected into the GSI, so we
could have omitted this property.I'll deploy the stack.
npx aws-cdk deploy
After deployment, the DynamoDB table has been provisioned and the global secondary index has been added to the table.
You can learn more about the related topics by checking out the following tutorials: