Skip to main content

Table

The Table construct is a higher level CDK construct that makes it easy to create a DynamoDB table. It uses the following defaults:

Examples

Primary index

import { Table } from "@serverless-stack/resources";

new Table(stack, "Notes", {
fields: {
userId: "string",
noteId: "string",
},
primaryIndex: { partitionKey: "noteId", sortKey: "userId" },
});

Global indexes

new Table(stack, "Notes", {
fields: {
userId: "string",
noteId: "string",
time: "number",
},
primaryIndex: { partitionKey: "noteId", sortKey: "userId" },
globalIndexes: {
userTimeIndex: { partitionKey: "userId", sortKey: "time" },
},
});

Configuring index projection

new Table(stack, "Table", {
fields: {
userId: "string",
noteId: "string",
time: "number",
},
primaryIndex: { partitionKey: "noteId", sortKey: "userId" },
globalIndexes: {
userTimeIndex: {
partitionKey: "userId",
sortKey: "time",
projection: "keys_only",
},
},
});

Local indexes

new Table(stack, "Notes", {
fields: {
userId: "string",
noteId: "string",
time: "number",
},
primaryIndex: { partitionKey: "noteId", sortKey: "userId" },
localIndexes: {
userTimeIndex: { sortKey: "time" },
},
});

DynamoDB Streams

Using the minimal config

Enable DynamoDB Streams and add consumers.

new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
stream: true,
consumers: {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
},
});

Lazily adding consumers

Lazily add the consumers after the table has been defined.

const table = new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
stream: true,
});

table.addConsumers(this, {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
});

Specifying function props for all the consumers

You can extend the minimal config, to set some function props and have them apply to all the consumers.

new Table(stack, "Notes", {
defaults: {
function: {
timeout: 20,
environment: { topicName: topic.topicName },
permissions: [topic],
},
},
stream: true,
consumers: {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
}
});

Using the full config

Configure each Lambda function separately.

new Table(stack, "Notes", {
stream: true,
consumers: {
consumer1: {
function: {
handler: "src/consumer1.main",
timeout: 10,
environment: { topicName: topic.topicName },
permissions: [topic],
},
}
},
});

Note that, you can set the defaults.function while using the function per consumer. The function will just override the defaults.function. Except for the environment, the layers, and the permissions properties, that will be merged.

new Table(stack, "Notes", {
defaults: {
function: {
timeout: 20,
environment: { topicName: topic.topicName },
permissions: [topic],
},
},
stream: true,
consumers: {
consumer1: {
function: {
handler: "src/consumer1.main",
timeout: 10,
environment: { bucketName: bucket.bucketName },
permissions: [bucket],
},
},
consumer2: "src/consumer2.main",
},
});

So in the above example, the consumer1 function doesn't use the timeout that is set in the defaults.function. It'll instead use the one that is defined in the function definition (10 seconds). And the function will have both the topicName and the bucketName environment variables set; as well as permissions to both the topic and the bucket.

Giving the consumers permissions

Allow the consumer functions to access S3.

const table = new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
stream: true,
consumers: {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
},
});

table.attachPermissions(["s3"]);

Giving a specific consumer permissions

Allow the first consumer function to access S3.

const table = new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
stream: true,
consumers: {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
},
});

table.attachPermissionsToConsumer("consumer1", ["s3"]);

Configuring the Stream content

Configure the information that will be written to the Stream.

new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
stream: "new_image",
consumers: {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
},
});

Configuring a consumer

Configure the internally created CDK Event Source.

import { StartingPosition } from "aws-cdk-lib/aws-lambda";

new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
stream: true,
consumers: {
consumer1: {
function: "src/consumer1.main",
cdk: {
eventSource: {
startingPosition: StartingPosition.TRIM_HORIZON,
},
},
},
},
});

Kinesis Streams

import { KinesisStream } from "@serverless-stack/resources";

const stream = new KinesisStream(this, "Stream");

new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
kinesisStream: stream,
});

Note, you do not need to configure the stream and consumers fields when enabling the Kinesis Streams. The stream field is used to configure DynamoDB Streams, and the consumers are only triggered by DynamoDB Streams.

You can read more about configuring consumers for the Kinesis Stream in the KinesisStream doc.

Advanced examples

Configuring the DynamoDB table

Configure the internally created CDK Table instance.

import { RemovalPolicy } from "aws-cdk-lib";

new Table(stack, "Table", {
fields: {
userId: "string",
noteId: "string",
},
primaryIndex: { partitionKey: "noteId", sortKey: "userId" },
cdk: {
table: {
removalPolicy: RemovalPolicy.DESTROY,
},
},
});

Importing an existing table

Override the internally created CDK Table instance.

import * as dynamodb from "aws-cdk-lib/aws-dynamodb";

new Table(stack, "Table", {
cdk: {
table: dynamodb.Table.fromTableArn(this, "ImportedTable", tableArn),
},
});

Enabling Global Tables

import { Duration } from "aws-cdk-lib";

const table = new Table(stack, "Notes", {
fields: {
noteId: "string",
},
primaryIndex: { partitionKey: "noteId" },
cdk: {
table: {
replicationRegions: ['us-east-1', 'us-east-2', 'us-west-2'],
replicationTimeout: Duration.hours(2),
},
},
});

Constructor

new Table(scope, id, props)

Parameters

TableProps

consumers?

Type : Record<string, string | Function | TableConsumerProps>

Configure DynamoDB streams and consumers

const table = new Table(stack, "Table", {
consumers: {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
},
});

defaults.function?

Type : FunctionProps

The default function props to be applied to all the consumers in the Table. The environment, permissions and layers properties will be merged with per route definitions if they are defined.

new Table(stack, "Table", {
defaults: {
function: {
timeout: 20,
environment: { topicName: topic.topicName },
permissions: [topic],
}
},
});

fields?

Type : Record<string, "string" | "number" | "binary">

An object defining the fields of the table. Key is the name of the field and the value is the type.

new Table(stack, "Table", {
fields: {
pk: "string",
sk: "string",
}
})

globalIndexes?

Type : Record<string, TableGlobalIndexProps>

Configure the table's global secondary indexes

new Table(stack, "Table", {
fields: {
pk: "string",
sk: "string",
gsi1pk: "string",
gsi1sk: "string",
},
globalIndexes: {
"GSI1": { partitionKey: "gsi1pk", sortKey: "gsi1sk" },
},
});

kinesisStream?

Type : KinesisStream

Configure the KinesisStream to capture item-level changes for the table.

const stream = new Table(stack, "Stream");

new Table(stack, "Table", {
kinesisStream: stream,
});

localIndexes?

Type : Record<string, TableLocalIndexProps>

Configure the table's local secondary indexes

new Table(stack, "Table", {
fields: {
pk: "string",
sk: "string",
lsi1sk: "string",
},
localIndexes: {
"lsi1": { sortKey: "lsi1sk" },
},
});

primaryIndex.partitionKey

Type : string

Define the Partition Key for the table's primary index

new Table(stack, "Table", {
fields: {
pk: "string",
},
primaryIndex: { partitionKey: "pk" },
});

primaryIndex.sortKey?

Type : string

Define the Sort Key for the table's primary index

new Table(stack, "Table", {
fields: {
pk: "string",
sk: "string",
},
primaryIndex: { partitionKey: "pk", sortKey: "sk" },
});

stream?

Type : boolean | "keys_only" | "new_image" | "old_image" | "new_and_old_images"

Configure the information that will be written to the Stream.

new Table(stack, "Table", {
stream: "new_image",
});

timeToLiveAttribute?

Type : string

The field that's used to store the expiration time for items in the table.

new Table(stack, "Table", {
timeToLiveAttribute: "expireAt",
});

cdk.table?

Type : ITable | Omit<TableProps, "sortKey" | "partitionKey">

Override the settings of the internally created cdk table

Properties

An instance of Table has the following properties.

tableArn

Type : string

The ARN of the internally created DynamoDB Table.

tableName

Type : string

The name of the internally created DynamoDB Table.

cdk.table

Type : ITable

The internally created CDK Table instance.

Methods

An instance of Table has the following methods.

addConsumers

addConsumers(scope, consumers)

Parameters

Define additional consumers for table events

table.addConsumers(stack, {
consumer1: "src/consumer1.main",
consumer2: "src/consumer2.main",
});

addGlobalIndexes

addGlobalIndexes(secondaryIndexes)

Parameters

Add additional global secondary indexes where the key is the name of the global secondary index

table.addGlobalIndexes({
gsi1: {
partitionKey: "pk",
sortKey: "sk",
}
})

addLocalIndexes

addLocalIndexes(secondaryIndexes)

Parameters

Add additional local secondary indexes where the key is the name of the local secondary index

table.addLocalIndexes({
lsi1: {
sortKey: "sk",
}
})

attachPermissions

attachPermissions(permissions)

Parameters

Grant permissions to all consumers of this table.

table.attachPermissions(["s3"]);

attachPermissionsToConsumer

attachPermissionsToConsumer(consumerName, permissions)

Parameters

Grant permissions to a specific consumer of this table.

table.attachPermissionsToConsumer("consumer1", ["s3"]);

getFunction

getFunction(consumerName)

Parameters

  • consumerName string

Get the instance of the internally created Function, for a given consumer.

 const table = new Table(stack, "Table", {
consumers: {
consumer1: "./src/function.handler",
}
})
table.getFunction("consumer1");

TableConsumerProps

function

Type : string | Function | FunctionProps

Used to create the consumer function for the table.

cdk.eventSource?

Type : DynamoEventSourceProps

Override the settings of the internally created event source

TableLocalIndexProps

projection?

Type : Array<string> | "all" | "keys_only"

Default : "all"

The set of attributes that are projected into the secondary index.

sortKey

Type : string

The field that's to be used as the sort key for the index.

cdk.index?

Type : Omit<LocalSecondaryIndexProps, "indexName" | "sortKey">

Override the settings of the internally created local secondary indexes

TableGlobalIndexProps

partitionKey

Type : string

The field that's to be used as a partition key for the index.

projection?

Type : Array<string> | "all" | "keys_only"

Default : "all"

The set of attributes that are projected into the secondary index.

sortKey?

Type : string

The field that's to be used as the sort key for the index.

cdk.index?

Type : Omit<GlobalSecondaryIndexProps, "indexName" | "sortKey" | "partitionKey">

Override the settings of the internally created global secondary index