or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cache-persistence.mdconfiguration.mddecorator.mddynamodb-persistence.mderrors.mdfunction-wrapper.mdindex.mdmiddleware.mdtypes.md
tile.json

dynamodb-persistence.mddocs/

DynamoDB Persistence

The DynamoDBPersistenceLayer class provides DynamoDB-backed storage for idempotency records. It uses the AWS SDK for JavaScript v3 to read and write records with extensive configuration options.

Capabilities

DynamoDB Persistence Layer Class

Stores idempotency records in Amazon DynamoDB with customizable table configuration and attribute names.

/**
 * DynamoDB persistence layer for idempotency records.
 *
 * Uses AWS SDK v3 to write and read idempotency records from DynamoDB.
 * Supports both simple (single key) and composite (partition + sort key) table structures.
 * Can use default client or bring your own configured client instance.
 *
 * @param config - Configuration options for DynamoDB persistence
 */
class DynamoDBPersistenceLayer extends BasePersistenceLayer {
  constructor(config: DynamoDBPersistenceOptions);
}

type DynamoDBPersistenceOptions =
  | DynamoDBPersistenceOptionsWithClientConfig
  | DynamoDBPersistenceOptionsWithClientInstance;

interface DynamoDBPersistenceOptionsWithClientConfig {
  /** DynamoDB table name (required) */
  tableName: string;
  /** Primary key attribute name (default: 'id') */
  keyAttr?: string;
  /** Sort key attribute name for composite keys (optional) */
  sortKeyAttr?: string;
  /** Static partition key value when using sort keys (default: 'idempotency#{LAMBDA_FUNCTION_NAME}') */
  staticPkValue?: string;
  /** Status attribute name (default: 'status') */
  statusAttr?: string;
  /** Expiry timestamp attribute name (default: 'expiration') */
  expiryAttr?: string;
  /** In-progress expiry timestamp attribute name (default: 'in_progress_expiration') */
  inProgressExpiryAttr?: string;
  /** Response data attribute name (default: 'data') */
  dataAttr?: string;
  /** Payload validation hash attribute name (default: 'validation') */
  validationKeyAttr?: string;
  /** Optional configuration for DynamoDB client initialization */
  clientConfig?: DynamoDBClientConfig;
  /** Must not be provided when using clientConfig */
  awsSdkV3Client?: never;
}

interface DynamoDBPersistenceOptionsWithClientInstance {
  tableName: string;
  keyAttr?: string;
  sortKeyAttr?: string;
  staticPkValue?: string;
  statusAttr?: string;
  expiryAttr?: string;
  inProgressExpiryAttr?: string;
  dataAttr?: string;
  validationKeyAttr?: string;
  /** Optional AWS SDK v3 DynamoDBClient instance */
  awsSdkV3Client?: DynamoDBClient;
  /** Must not be provided when using awsSdkV3Client */
  clientConfig?: never;
}

Usage Examples:

Basic Setup with Default Configuration

import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';

// Minimal configuration - uses default attribute names and creates a new client
const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'idempotencyTable',
});

const myHandler = async (event: any, context: any) => {
  // Your logic
};

export const handler = makeIdempotent(myHandler, { persistenceStore });

With Custom Attribute Names

import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';

const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'idempotencyTable',
  keyAttr: 'requestId',           // Custom primary key attribute
  statusAttr: 'requestStatus',     // Custom status attribute
  expiryAttr: 'ttl',              // Custom expiry attribute
  dataAttr: 'responseData',        // Custom data attribute
  validationKeyAttr: 'payloadHash', // Custom validation attribute
});

const myHandler = async (event: any, context: any) => {
  // Your logic
};

export const handler = makeIdempotent(myHandler, { persistenceStore });

With Client Configuration (AWS Region)

import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';

// Specify AWS region and other client configuration
const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'idempotencyTable',
  clientConfig: {
    region: 'us-east-1',
  },
});

const myHandler = async (event: any, context: any) => {
  // Your logic
};

export const handler = makeIdempotent(myHandler, { persistenceStore });

With Custom DynamoDB Client

import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';

// Create and configure your own DynamoDB client
const dynamoDBClient = new DynamoDBClient({
  region: 'us-west-2',
  maxAttempts: 3,
  requestHandler: {
    // Custom configurations
  },
});

const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'idempotencyTable',
  awsSdkV3Client: dynamoDBClient,
});

const myHandler = async (event: any, context: any) => {
  // Your logic
};

export const handler = makeIdempotent(myHandler, { persistenceStore });

With Composite Key (Partition + Sort Key)

import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';

// Use a table with both partition key and sort key
const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'idempotencyTable',
  keyAttr: 'pk',                    // Partition key attribute
  sortKeyAttr: 'sk',                 // Sort key attribute
  staticPkValue: 'idempotency#my-function', // Static value for partition key
});

// With composite keys:
// - pk (partition key) = staticPkValue
// - sk (sort key) = hashed idempotency key

const myHandler = async (event: any, context: any) => {
  // Your logic
};

export const handler = makeIdempotent(myHandler, { persistenceStore });

Shared Table Pattern

import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';

// Share a single DynamoDB table across multiple functions using sort keys
const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'shared-idempotency-table',
  keyAttr: 'pk',
  sortKeyAttr: 'sk',
  staticPkValue: 'payment-service', // Unique prefix for this service
});

const myHandler = async (event: any, context: any) => {
  // Your logic
};

export const handler = makeIdempotent(myHandler, { persistenceStore });

DynamoDB Table Structure

Simple Key Structure (Default)

When using only keyAttr (no sortKeyAttr):

{
  "id": "my-function#a1b2c3d4...",        // Hashed idempotency key
  "status": "COMPLETED",                   // INPROGRESS | COMPLETED | EXPIRED
  "expiration": 1234567890,                // TTL in seconds (Unix epoch)
  "in_progress_expiration": 1234567890000, // Lambda timeout in milliseconds
  "data": { ... },                         // Response data
  "validation": "e5f6g7h8..."             // Payload hash (if validation enabled)
}

Composite Key Structure

When using both keyAttr and sortKeyAttr:

{
  "pk": "idempotency#my-function",         // Static partition key value
  "sk": "a1b2c3d4...",                     // Hashed idempotency key (sort key)
  "status": "COMPLETED",
  "expiration": 1234567890,
  "in_progress_expiration": 1234567890000,
  "data": { ... },
  "validation": "e5f6g7h8..."
}

Required IAM Permissions

Your Lambda function needs the following DynamoDB permissions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem",
        "dynamodb:DeleteItem"
      ],
      "Resource": "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/TABLE_NAME"
    }
  ]
}

DynamoDB Table Configuration

Time to Live (TTL)

Enable TTL on the expiry attribute to automatically delete expired records:

  1. Go to your DynamoDB table in the AWS Console
  2. Navigate to "Additional settings" → "Time to Live"
  3. Enable TTL on the attribute name matching expiryAttr (default: "expiration")

Table Capacity

  • On-Demand: Recommended for variable workloads
  • Provisioned: Calculate based on:
    • Write capacity: One write per unique request
    • Read capacity: One strongly consistent read per duplicate request

Global Secondary Indexes

Not required for basic idempotency functionality. The persistence layer uses GetItem and PutItem operations on the primary key.

Conditional Write Logic

The DynamoDB persistence layer uses conditional expressions to prevent race conditions:

  1. New Record: Creates record only if idempotency key doesn't exist
  2. Expired Record: Overwrites if existing record is expired
  3. Orphaned Record: Overwrites if "in progress" record has exceeded Lambda timeout
  4. Active Record: Throws IdempotencyItemAlreadyExistsError for active duplicates

Error Handling

  • IdempotencyItemNotFoundError - Record not found during retrieval
  • IdempotencyItemAlreadyExistsError - Duplicate request detected with active record
  • Throws if sortKeyAttr equals keyAttr (invalid configuration)
  • Logs warning if provided client is not a valid AWS SDK v3 client