Idempotency Table
A CDK construct that provisions a DynamoDB table optimized for AWS Lambda Powertools idempotency functionality, complete with:
- Proper partition key configuration (
id
as STRING) - Built-in TTL support with
expiration
attribute - AWS-managed encryption by default
- Sensible defaults for cost optimization
- Point-in-time recovery configuration options
- cdk-nag compliance with targeted suppressions
- Jest unit tests with comprehensive validation
Features
- Table Schema: DynamoDB table with mandatory idempotency configuration:
partitionKey: { name: 'id', type: STRING }
(required by Powertools)timeToLiveAttribute: 'expiration'
(automatic clean-up)- No sort key (simple primary key only)
- Security: AWS-managed encryption enabled by default
- Cost Optimization: Point-in-time recovery disabled by default, contributor insights disabled
- Flexibility: All other DynamoDB table properties are customizable
- Compliance: Built-in cdk-nag compatibility with appropriate suppressions
- Testing: Comprehensive Jest test suite with CloudFormation template validation
Usage
Basic Usage
import { IdempotencyTable } from '@leighton-digital/cloud-blocks';
// Minimal configuration
const idempotencyTable = new IdempotencyTable(this, 'IdempotencyTable', {
tableName: 'my-function-idempotency'
});
// Access the underlying DynamoDB table
const table = idempotencyTable.table;
Advanced Configuration
import * as cdk from 'aws-cdk-lib';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { IdempotencyTable } from '@leighton-digital/cloud-blocks';
const customTable = new IdempotencyTable(this, 'CustomIdempotencyTable', {
tableName: 'custom-idempotency-table',
removalPolicy: cdk.RemovalPolicy.RETAIN,
// Capacity configuration
billingMode: dynamodb.BillingMode.PROVISIONED,
readCapacity: 10,
writeCapacity: 10,
// Advanced features
pointInTimeRecoverySpecification: {
pointInTimeRecoveryEnabled: true,
},
contributorInsightsSpecification: {
enabled: true,
},
// Encryption
encryption: dynamodb.TableEncryption.CUSTOMER_MANAGED,
encryptionKey: myKmsKey,
// Deletion protection
deletionProtection: true,
});
Lambda Function Integration
import { Code, Function, Runtime} from 'aws-cdk-lib/aws-lambda';
import { IdempotencyTable } from '@leighton-digital/cloud-blocks';
const idempotencyTable = new IdempotencyTable(this, 'IdempotencyTable', {
tableName: 'my-lambda-idempotency'
});
const myFunction = new Function(this, 'MyFunction', {
runtime: Runtime.PYTHON_3_11,
handler: 'index.handler',
code: Code.fromAsset('lambda'),
environment: {
IDEMPOTENCY_TABLE_NAME: idempotencyTable.table.tableName,
},
});
// Grant read/write permissions to the Lambda function
idempotencyTable.table.grantReadWriteData(myFunction);
Props
All of the props are inherited from the DynamoDB Table construct, except for the following:
partitionKey
: Always{ name: 'id', type: STRING }
timeToLiveAttribute
: Always'expiration'
sortKey
: Not allowed (simple primary key only)
The above props are automatically set and cannot be customized as they are required for AWS Lambda Powertools compatibility.
What gets created
- DynamoDB Table:
IdempTable${constructId}
- Partition key:
id
(STRING) - TTL attribute:
expiration
- AWS-managed encryption by default
- Uses CDK default billing mode
- Point-in-time recovery disabled by default (cost optimization)
- Partition key:
AWS Lambda Powertools Integration
This construct is specifically designed to work with the AWS Lambda Powertools (TypeScript) - Idempotency utility:
Example
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: process.env.IDEMPOTENCY_TABLE_NAME!,
});
export const handler = makeIdempotent(
async (event: any, context: any) => {
// Your idempotent function logic here
return { statusCode: 200, body: 'Success' };
},
{ persistenceStore }
);
Cost Optimization
The construct includes several cost optimization features enabled by default:
- Pay-per-request billing: No upfront capacity costs
- Point-in-time recovery disabled: Reduces costs for dev/test environments
- Contributor insights disabled: Avoids additional CloudWatch charges
- TTL enabled: Automatic clean-up of expired records reduces storage costs
For production environments, consider:
const prodTable = new IdempotencyTable(this, 'ProdIdempotencyTable', {
tableName: 'prod-idempotency-table',
removalPolicy: cdk.RemovalPolicy.RETAIN,
pointInTimeRecoverySpecification: {
pointInTimeRecoveryEnabled: true,
},
deletionProtection: true, // Prevent accidental deletion
});
Operational notes & caveats
- Schema constraints: The table schema is fixed to ensure compatibility with AWS Lambda Powertools. You cannot modify the partition key, TTL attribute, or add a sort key.
- TTL behaviour: Records with an
expiration
timestamp in the past will be automatically deleted by DynamoDB (typically within 48 hours). - Removal policy: Uses CDK default behaviour. Explicitly set
RETAIN
for production safety orDESTROY
for development environments. - Billing mode: Uses CDK default (pay-per-request), which is cost-effective for variable workloads. Switch to provisioned if you have predictable traffic patterns.
Extending
- Monitoring: Add CloudWatch alarms for throttling, errors, or capacity metrics
- Global tables: Configure global tables for multi-region deployments
- Backup: Configure automated backups for compliance requirements
- Indexes: Add global secondary indexes if you need additional query patterns (though this is uncommon for idempotency use cases)
Troubleshooting
- "Cannot override partition key" → The partition key is fixed to
'id'
(STRING) for Powertools compatibility - "TTL attribute is required" → The
expiration
TTL attribute is mandatory and cannot be changed - "Sort key not supported" → Idempotency tables use a simple primary key only
- Lambda permission errors → Ensure you've granted
grantReadWriteData()
permissions for the table to your Lambda function - Powertools not finding table → Verify the table name environment variable matches your table's name