stores/dynamodb/TABLE_SETUP.md
This document explains how to set up the required DynamoDB table structure for use with the @mastra/dynamodb package.
The @mastra/dynamodb package uses a single-table design pattern with ElectroDB. You need to create a single DynamoDB table with the following structure:
DynamoDBStore constructorpk (String)sk (String)gsi1pk (String)gsi1sk (String)gsi2pk (String)gsi2sk (String)(Note: These GSIs (Global Secondary Indexes) allow efficient querying on attributes other than the primary key, enabling different data access patterns required by Mastra components.)
GSI1 (Index Name: gsi1): This index is used by multiple entities for common lookup patterns:
threadEntity: Query by resourceId (byResource index).messageEntity: Query by threadId (byThread index).traceEntity: Query by name (byName index).evalEntity: Query by agent_name (byAgent index).GSI2 (Index Name: gsi2): This index is used for:
traceEntity: Query by scope (byScope index).workflowSnapshotEntity: Query by run_id (byRunId index).Here's an example CloudFormation template for creating the required table, reflecting the GSI usage:
Resources:
MastraSingleTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: mastra-single-table
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
- AttributeName: sk
AttributeType: S
- AttributeName: gsi1pk
AttributeType: S
- AttributeName: gsi1sk
AttributeType: S
- AttributeName: gsi2pk
AttributeType: S
- AttributeName: gsi2sk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: gsi1
KeySchema:
- AttributeName: gsi1pk
KeyType: HASH
- AttributeName: gsi1sk
KeyType: RANGE
Projection:
ProjectionType: ALL # Suitable for varied query needs of GSI1
- IndexName: gsi2
KeySchema:
- AttributeName: gsi2pk
KeyType: HASH
- AttributeName: gsi2sk
KeyType: RANGE
Projection:
ProjectionType: ALL
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
SSESpecification:
SSEEnabled: true
Here's how to create the table using AWS CDK:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
export class MastraDynamoDbStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Consider parameterizing the table name for different environments
const tableName = 'mastra-single-table';
// Create the single table
const table = new dynamodb.Table(this, 'MastraSingleTable', {
tableName: tableName,
partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: true,
encryption: dynamodb.TableEncryption.AWS_MANAGED,
});
// Add GSI1
table.addGlobalSecondaryIndex({
indexName: 'gsi1',
partitionKey: { name: 'gsi1pk', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'gsi1sk', type: dynamodb.AttributeType.STRING },
// projectionType defaults to ALL in CDK, which is suitable for flexible querying but has cost implications.
});
// Add GSI2 (Used by Trace and WorkflowSnapshot)
table.addGlobalSecondaryIndex({
indexName: 'gsi2',
partitionKey: { name: 'gsi2pk', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'gsi2sk', type: dynamodb.AttributeType.STRING },
// projectionType defaults to ALL in CDK
});
}
}
If you want to use automatic data expiration with DynamoDB TTL, you need to enable it on your table. The @mastra/dynamodb package supports configuring TTL per entity type (threads, messages, traces, etc.).
Add the TimeToLiveSpecification to your table definition:
Resources:
MastraSingleTable:
Type: AWS::DynamoDB::Table
Properties:
# ... other properties ...
TimeToLiveSpecification:
AttributeName: ttl # Must match config.ttl.[entity].attributeName (default: 'ttl')
Enabled: true
Enable TTL when creating the table:
const table = new dynamodb.Table(this, 'MastraSingleTable', {
// ... other properties ...
timeToLiveAttribute: 'ttl', // Must match config.ttl.[entity].attributeName (default: 'ttl')
});
Enable TTL on an existing table:
aws dynamodb update-time-to-live \
--table-name mastra-single-table \
--time-to-live-specification "Enabled=true, AttributeName=ttl"
After enabling TTL on your table, configure which entity types should use TTL:
const storage = new DynamoDBStore({
name: 'dynamodb',
config: {
tableName: 'mastra-single-table',
region: 'us-east-1',
ttl: {
message: {
enabled: true,
defaultTtlSeconds: 30 * 24 * 60 * 60, // 30 days
},
trace: {
enabled: true,
defaultTtlSeconds: 7 * 24 * 60 * 60, // 7 days
},
},
},
});
Note: DynamoDB TTL deletes expired items within 48 hours after expiration. Items remain queryable until actually deleted.
Once the table is created, you can use it with the DynamoDBStore:
import { Memory } from '@mastra/memory';
import { DynamoDBStore } from '@mastra/dynamodb';
import { PineconeVector } from '@mastra/pinecone';
const storage = new DynamoDBStore({
name: 'dynamodb',
config: {
region: 'us-east-1',
tableName: 'mastra-single-table', // use the name you chose when creating the table
},
});
const vector = new PineconeVector({
id: 'dynamodb-pinecone',
apiKey: process.env.PINECONE_API_KEY,
});
const memory = new Memory({
storage,
vector,
options: {
lastMessages: 10,
semanticRecall: true,
},
});
For local development, you can use the AWS DynamoDB Local Docker image:
docker run -p 8000:8000 amazon/dynamodb-local
And then point your DynamoDBStore to the local instance:
const storage = new DynamoDBStore({
name: 'dynamodb',
config: {
region: 'us-east-1',
tableName: 'mastra-single-table',
endpoint: 'http://localhost:8000', // Local DynamoDB endpoint
},
});