Configure DynamoDB in CDK
We are now going to start creating our resources using CDK. Starting with DynamoDB.
Create a Stack
Add the following to infrastructure/lib/DynamoDBStack.js
.
import { CfnOutput } from "@aws-cdk/core";
import * as dynamodb from "@aws-cdk/aws-dynamodb";
import * as sst from "@serverless-stack/resources";
export default class DynamoDBStack extends sst.Stack {
constructor(scope, id, props) {
super(scope, id, props);
const app = this.node.root;
const table = new dynamodb.Table(this, "Table", {
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // Use on-demand billing mode
sortKey: { name: "noteId", type: dynamodb.AttributeType.STRING },
partitionKey: { name: "userId", type: dynamodb.AttributeType.STRING },
});
// Output values
new CfnOutput(this, "TableName", {
value: table.tableName,
exportName: app.logicalPrefixedName("TableName"),
});
new CfnOutput(this, "TableArn", {
value: table.tableArn,
exportName: app.logicalPrefixedName("TableArn"),
});
}
}
Let’s quickly go over what we are doing here.
-
We are creating a new stack for our DynamoDB table by extending
sst.Stack
instead ofcdk.Stack
. This is what allows us to deploy CDK alongside our Serverless services. -
We are defining the table we created back in the Create a DynamoDB Table chapter. By default, CDK will generate a table name for us.
-
We add
userId
as apartitionKey
andnoteId
as oursortKey
. -
We also set our
BillingMode
toPAY_PER_REQUEST
. This is the On-demand option that we had selected in the AWS console. -
We need to use the table name in our API. Also, we’ll use the table ARN to ensure that our Lambda functions have access to this table. We don’t want to hardcode these values. So we’ll use a CloudFormation export, using the
CfnOutput
method with theexportName
option. We’ll later import these values in our API using cross-stack references. The output names need to be unique per stack. While theexportName
needs to be unique for a given region in the AWS account. To ensure that it’ll be unique when we deploy to multiple environments, we’ll use theapp.logicalPrefixedName
method. It’s a convenience method insst.App
that prefixes a given name with the name of the stage (environment) and the name of the app. We’ll use this method whenever we need to ensure uniqueness across environments.
You can refer to the CDK docs for more details on the dynamodb.Table construct.
Note that, we don’t need to create a separate stack for each resource. We could use a single stack for all our resources. But for the purpose of illustration, we are going to split them all up.
Let’s add the DynamoDB CDK package. Run the following in your infrastructure/
directory.
$ npx sst add-cdk @aws-cdk/aws-dynamodb
The reason we are using the add-cdk command instead of using an npm install
, is because of a known issue with AWS CDK. Using mismatched versions of CDK packages can cause some unexpected problems down the road. The sst add-cdk
command ensures that we install the right version of the package.
Add the Stack
Now let’s add this stack to our app.
Replace your infrastructure/lib/index.js
with this.
import DynamoDBStack from "./DynamoDBStack";
// Add stacks
export default function main(app) {
new DynamoDBStack(app, "dynamodb");
}
We are now ready to deploy the DynamoDB stack in our app.
Deploy the Stack
To deploy your app run the following in the infrastructure/
directory.
$ npx sst deploy
You should see something like this at the end of the deploy process.
Stack dev-notes-infra-dynamodb
Status: deployed
Outputs:
TableName: dev-notes-infra-dynamodb-TableCD117FA1-RBR93WLG5IQH
TableArn: arn:aws:dynamodb:us-east-1:087220554750:table/dev-notes-infra-dynamodb-TableCD117FA1-RBR93WLG5IQH
Exports:
dev-notes-infra-TableName: dev-notes-infra-dynamodb-TableCD117FA1-RBR93WLG5IQH
dev-notes-infra-TableArn: arn:aws:dynamodb:us-east-1:087220554750:table/dev-notes-infra-dynamodb-TableCD117FA1-RBR93WLG5IQH
You’ll notice the table name and ARN in the output and exported values.
Note that, we created a completely new DynamoDB table here. If you want to remove the old table we created manually through the console, you can do so now. We are going to leave it as is, in case you want to refer back to it at some point.
Remove Template Files
There are a couple of files that come with the template, that we can now remove.
Run this from the infrastructure/
directory.
$ rm lib/MyStack.js
$ rm README.md
Fix the Unit Tests
You can also setup unit tests for your stacks. We’ll add a simple one here to show you how it works.
Start by renaming the infrastructure/MyStack.test.js
.
$ mv test/MyStack.test.js test/DynamoDBStack.test.js
And replace infrastructure/test/DynamoDBStack.test.js
with.
import { expect, haveResource } from "@aws-cdk/assert";
import * as sst from "@serverless-stack/resources";
import DynamoDBStack from "../lib/DynamoDBStack";
test("Test Stack", () => {
const app = new sst.App();
// WHEN
const stack = new DynamoDBStack(app, "test-stack");
// THEN
expect(stack).to(
haveResource("AWS::DynamoDB::Table", {
BillingMode: "PAY_PER_REQUEST",
})
);
});
This is a really simple test that ensure that our DynamoDBStack
class is creating a DynamoDB table with the BillingMode
set to PAY_PER_REQUEST
.
And we can run the test using.
$ npx sst test
You should see something like this as your test output.
PASS test/DynamoDBStack.test.js
✓ Test Stack (1022 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.473 s
Ran all test suites.
You can build on these tests later when you stack becomes more complicated.
For now let’s move on to S3 and create a bucket for file uploads.
For help and discussion
Comments on this chapter