Skip to content

Dynamo

Reference doc for the `sst.aws.Dynamo` component.

The Dynamo component lets you add an Amazon DynamoDB table to your app.

Minimal example

sst.config.ts
const table = new sst.aws.Dynamo("MyTable", {
fields: {
userId: "string",
noteId: "string"
},
primaryIndex: { hashKey: "userId", rangeKey: "noteId" }
});

Add a global index

Optionally add a global index to the table.

sst.config.ts
new sst.aws.Dynamo("MyTable", {
fields: {
userId: "string",
noteId: "string",
createdAt: "number",
},
primaryIndex: { hashKey: "userId", rangeKey: "noteId" },
globalIndexes: {
CreatedAtIndex: { hashKey: "userId", rangeKey: "createdAt" }
}
});

Add a local index

Optionally add a local index to the table.

sst.config.ts
new sst.aws.Dynamo("MyTable", {
fields: {
userId: "string",
noteId: "string",
createdAt: "number",
},
primaryIndex: { hashKey: "userId", rangeKey: "noteId" },
localIndexes: {
CreatedAtIndex: { rangeKey: "createdAt" }
}
});

Subscribe to a DynamoDB Stream

To subscribe to a DynamoDB Stream, start by enabling it.

sst.config.ts
const table = new sst.aws.Dynamo("MyTable", {
fields: {
userId: "string",
noteId: "string"
},
primaryIndex: { hashKey: "userId", rangeKey: "noteId" },
stream: "new-and-old-images"
});

Then, subscribing to it.

sst.config.ts
table.subscribe("MySubscriber", "src/subscriber.handler");

You can link the table to other resources, like a function or your Next.js app.

sst.config.ts
new sst.aws.Nextjs("MyWeb", {
link: [table]
});

Once linked, you can query the table through your app.

app/page.tsx
import { Resource } from "sst";
import { DynamoDBClient, QueryCommand, ScanCommand } from "@aws-sdk/client-dynamodb";
const client = new DynamoDBClient();
await client.send(new QueryCommand({
TableName: Resource.MyTable.name,
KeyConditionExpression: "userId = :userId",
ExpressionAttributeValues: {
":userId": "my-user-id"
}
}));

Constructor

new Dynamo(name, args, opts?)

Parameters

DynamoArgs

deletionProtection?

Type Input<boolean>

Enable deletion protection for the table. When enabled, the table cannot be deleted.

{
deletionProtection: true,
}

fields

Type Input<Record<string, string | number | binary>>

An object defining the fields of the table that’ll be used to create indexes. The key is the name of the field and the value is the type.

While you can have fields field types other than string, number, and binary; you can only use these types for your indexes.

{
fields: {
userId: "string",
noteId: "string"
}
}

globalIndexes?

Type Input<Record<string, Input<Object>>>

Configure the table’s global secondary indexes.

You can have up to 20 global secondary indexes per table. And each global secondary index should have a unique name.

{
globalIndexes: {
CreatedAtIndex: { hashKey: "userId", rangeKey: "createdAt" }
}
}

globalIndexes[].hashKey

Type Input<string>

The hash key field of the index. This field needs to be defined in the fields.

globalIndexes[].projection?

Type Input<Input<string>[] | all | keys-only>

Default “all”

The fields to project into the index.

Project only the key fields: userId and createdAt.

{
hashKey: "userId",
rangeKey: "createdAt",
projection: "keys-only"
}

Project the noteId field in addition to the key fields.

{
hashKey: "userId",
rangeKey: "createdAt",
projection: ["noteId"]
}

globalIndexes[].rangeKey?

Type Input<string>

The range key field of the index. This field needs to be defined in the fields.

localIndexes?

Type Input<Record<string, Input<Object>>>

Configure the table’s local secondary indexes.

Unlike global indexes, local indexes use the same hashKey as the primaryIndex of the table.

You can have up to 5 local secondary indexes per table. And each local secondary index should have a unique name.

{
localIndexes: {
CreatedAtIndex: { rangeKey: "createdAt" }
}
}

localIndexes[].projection?

Type Input<Input<string>[] | all | keys-only>

Default “all”

The fields to project into the index.

Project only the key field: createdAt.

{
rangeKey: "createdAt",
projection: "keys-only"
}

Project the noteId field in addition to the key field.

{
rangeKey: "createdAt",
projection: ["noteId"]
}

localIndexes[].rangeKey

Type Input<string>

The range key field of the index. This field needs to be defined in the fields.

primaryIndex

Type Input<Object>

Define the table’s primary index. You can only have one primary index.

{
primaryIndex: { hashKey: "userId", rangeKey: "noteId" }
}

primaryIndex.hashKey

Type Input<string>

The hash key field of the index. This field needs to be defined in the fields.

primaryIndex.rangeKey?

Type Input<string>

The range key field of the index. This field needs to be defined in the fields.

stream?

Type Input<keys-only | new-image | old-image | new-and-old-images>

Default Disabled

Enable DynamoDB Streams for the table.

When an item in the table is modified, the stream captures the information and sends it to your subscriber function.

You can configure what will be written to the stream:

  • new-image: The entire item after it was modified.
  • old-image: The entire item before it was modified.
  • new-and-old-images: Both the new and the old items. A good default to use since it contains all the data.
  • keys-only: Only the keys of the fields of the modified items. If you are worried about the costs, you can use this since it stores the least amount of data.
{
stream: "new-and-old-images"
}

transform?

Type Object

Transform how this component creates its underlying resources.

transform.table?

Type TableArgs | (args: TableArgs, opts: ComponentResourceOptions, name: string) => void

Transform the DynamoDB Table resource.

ttl?

Type Input<string>

The field in the table to store the Time to Live or TTL timestamp in. This field should be of type number. When the TTL timestamp is reached, the item will be deleted.

Read more about Time to Live.

Here the TTL field in our table is called expireAt.

{
ttl: "expireAt"
}

Properties

arn

Type Output<string>

The ARN of the DynamoDB Table.

name

Type Output<string>

The name of the DynamoDB Table.

nodes

Type Object

The underlying resources this component creates.

nodes.table

Type Output<Table>

The Amazon DynamoDB Table.

SDK

Use the SDK in your runtime to interact with your infrastructure.


This is accessible through the Resource object in the SDK.

  • name string

    The name of the DynamoDB Table.

Methods

subscribe

subscribe(name, subscriber, args?)

Parameters

  • name string

    The name of the subscriber.
  • subscriber Input<string | FunctionArgs | “arn:aws:lambda:${string}”>

    The function that’ll be notified.
  • args? DynamoSubscriberArgs

    Configure the subscription.

Returns Output<DynamoLambdaSubscriber>

Subscribe to the DynamoDB Stream of this table.

sst.config.ts
table.subscribe("MySubscriber", "src/subscriber.handler");

Add a filter to the subscription.

sst.config.ts
table.subscribe("MySubscriber", "src/subscriber.handler", {
filters: [
{
dynamodb: {
Keys: {
CustomerName: {
S: ["AnyCompany Industries"]
}
}
}
}
]
});

Customize the subscriber function.

sst.config.ts
table.subscribe("MySubscriber", {
handler: "src/subscriber.handler",
timeout: "60 seconds"
});

Or pass in the ARN of an existing Lambda function.

sst.config.ts
table.subscribe("MySubscriber", "arn:aws:lambda:us-east-1:123456789012:function:my-function");

static get

Dynamo.get(name, tableName, opts?)

Parameters

  • name string

    The name of the component.
  • tableName Input<string>

    The name of the DynamoDB Table.
  • opts? ComponentResourceOptions

Returns Dynamo

Reference an existing DynamoDB Table with the given table name. This is useful when you create a table in one stage and want to share it in another stage. It avoid having to create a new table in the other stage.

Imagine you create a table in the dev stage. And in your personal stage frank, instead of creating a new table, you want to share the table from dev.

sst.config.ts"
const table = $app.stage === "frank"
? sst.aws.Dynamo.get("MyTable", "app-dev-mytable")
: new sst.aws.Dynamo("MyTable");

Here app-dev-mytable is the name of the DynamoDB Table created in the dev stage. You can find this by outputting the table name in the dev stage.

sst.config.ts
return {
table: table.name
};

static subscribe

Dynamo.subscribe(name, streamArn, subscriber, args?)

Parameters

  • name string

    The name of the subscriber.
  • streamArn Input<string>

    The ARN of the DynamoDB Stream to subscribe to.
  • subscriber Input<string | FunctionArgs | “arn:aws:lambda:${string}”>

    The function that’ll be notified.
  • args? DynamoSubscriberArgs

    Configure the subscription.

Returns Output<DynamoLambdaSubscriber>

Subscribe to the DynamoDB stream of a table that was not created in your app.

For example, let’s say you have a DynamoDB stream ARN of an existing table.

sst.config.ts
const streamArn = "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable/stream/2024-02-25T23:17:55.264";

You can subscribe to it by passing in the ARN.

sst.config.ts
sst.aws.Dynamo.subscribe("MySubscriber", streamArn, "src/subscriber.handler");

Add a filter to the subscription.

sst.config.ts
sst.aws.Dynamo.subscribe("MySubscriber", streamArn, "src/subscriber.handler", {
filters: [
{
dynamodb: {
Keys: {
CustomerName: {
S: ["AnyCompany Industries"]
}
}
}
}
]
});

Customize the subscriber function.

sst.config.ts
sst.aws.Dynamo.subscribe("MySubscriber", streamArn, {
handler: "src/subscriber.handler",
timeout: "60 seconds"
});

DynamoSubscriberArgs

filters?

Type Input<Input<Record<string, any>>[]>

Filter the records processed by the subscriber function.

You can pass in up to 5 different filter policies. These will logically ORed together. Meaning that if any single policy matches, the record will be processed.

For example, if your DynamoDB table’s stream contains the follow record.

{
eventID: "1",
eventVersion: "1.0",
dynamodb: {
ApproximateCreationDateTime: "1678831218.0",
Keys: {
CustomerName: {
"S": "AnyCompany Industries"
},
NewImage: {
AccountManager: {
S: "Pat Candella"
},
PaymentTerms: {
S: "60 days"
},
CustomerName: {
S: "AnyCompany Industries"
}
},
SequenceNumber: "111",
SizeBytes: 26,
StreamViewType: "NEW_IMAGE"
}
}
}

To process only those records where the CustomerName is AnyCompany Industries.

{
filters: [
{
dynamodb: {
Keys: {
CustomerName: {
S: ["AnyCompany Industries"]
}
}
}
}
]
}

transform?

Type Object

Transform how this subscription creates its underlying resources.

transform.eventSourceMapping?

Type EventSourceMappingArgs | (args: EventSourceMappingArgs, opts: ComponentResourceOptions, name: string) => void

Transform the Lambda Event Source Mapping resource.