CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-aws-lambda-powertools--idempotency

Idempotency utility for AWS Lambda functions that prevents duplicate executions by tracking request payloads in DynamoDB or cache stores, with support for function wrappers, decorators, and Middy middleware.

Overview
Eval results
Files

types.mddocs/

Type Definitions

Core type definitions for idempotency options, persistence configuration, record management, and interfaces.

Capabilities

Idempotency Record Types

Types for creating and managing idempotency records.

/**
 * Options for creating a new IdempotencyRecord
 */
interface IdempotencyRecordOptions {
  /** The idempotency key used to identify the record */
  idempotencyKey: string;
  /** The record status (INPROGRESS | COMPLETED | EXPIRED) */
  status: IdempotencyRecordStatusValue;
  /** Optional sort key for composite key tables */
  sortKey?: string;
  /** Expiry timestamp in seconds (Unix epoch) */
  expiryTimestamp?: number;
  /** In-progress expiry timestamp in milliseconds (Unix epoch) */
  inProgressExpiryTimestamp?: number;
  /** Response data to return for idempotent requests */
  responseData?: JSONValue;
  /** Hash of the payload for validation */
  payloadHash?: string;
}

/**
 * The status of an IdempotencyRecord
 */
type IdempotencyRecordStatusValue =
  | "INPROGRESS"
  | "COMPLETED"
  | "EXPIRED";

Base Persistence Types

Types for configuring and implementing persistence layers.

/**
 * Base attributes used by persistence layers (DynamoDB, Redis, etc.)
 */
interface BasePersistenceAttributes {
  /** Attribute name for expiry timestamp (default: 'expiration') */
  expiryAttr?: string;
  /** Attribute name for in-progress expiry timestamp (default: 'in_progress_expiration') */
  inProgressExpiryAttr?: string;
  /** Attribute name for status (default: 'status') */
  statusAttr?: string;
  /** Attribute name for response data (default: 'data') */
  dataAttr?: string;
  /** Attribute name for validation hash (default: 'validation') */
  validationKeyAttr?: string;
}

/**
 * Options for configuring a persistence layer
 */
interface BasePersistenceLayerOptions {
  /** IdempotencyConfig instance */
  config: IdempotencyConfig;
  /** Optional function name for key prefix */
  functionName?: string;
  /** Optional custom prefix for idempotency keys */
  keyPrefix?: string;
}

/**
 * Interface that all persistence layers must implement
 */
interface BasePersistenceLayerInterface {
  /** Initialize the persistence layer with configuration */
  configure(options?: BasePersistenceLayerOptions): void;
  /** Check if payload validation is enabled */
  isPayloadValidationEnabled(): boolean;
  /** Save a record indicating execution is in progress */
  saveInProgress(data: unknown, remainingTimeInMillis?: number): Promise<void>;
  /** Save a record indicating successful completion */
  saveSuccess(data: unknown, result: unknown): Promise<void>;
  /** Delete an idempotency record */
  deleteRecord(data: unknown): Promise<void>;
  /** Retrieve an idempotency record */
  getRecord(data: unknown): Promise<IdempotencyRecord>;
}

Function Wrapper Types

Types for configuring idempotent functions.

/**
 * Generic function type for any function signature
 */
type AnyFunction = (...args: Array<any>) => any;

/**
 * Conditional type for makeIdempotent and idempotent options.
 * Detects Lambda handlers (functions with Context as second argument)
 * and allows dataIndexArgument for non-handler functions.
 */
type ItempotentFunctionOptions<T extends Array<any>> = T[1] extends Context
  ? IdempotencyLambdaHandlerOptions
  : IdempotencyLambdaHandlerOptions & {
      /** Index of the argument to use as idempotency payload (zero-based) */
      dataIndexArgument?: number;
    };

/**
 * Configuration options for idempotency with Lambda handlers
 */
interface IdempotencyLambdaHandlerOptions {
  /** Persistence layer to store idempotency records */
  persistenceStore: BasePersistenceLayer;
  /** Optional configuration for idempotency behavior */
  config?: IdempotencyConfig;
  /** Optional custom prefix for idempotency keys */
  keyPrefix?: string;
}

/**
 * A hook that runs when an idempotent request is made
 */
type ResponseHook = (
  response: JSONValue,
  record: IdempotencyRecord
) => JSONValue;

Configuration Types

Types for configuring idempotency behavior.

/**
 * Configuration options for IdempotencyConfig
 */
interface IdempotencyConfigOptions {
  /** JMESPath expression to extract idempotency key from event */
  eventKeyJmesPath?: string;
  /** JMESPath expression to extract payload for validation */
  payloadValidationJmesPath?: string;
  /** Custom JMESPath functions */
  jmesPathOptions?: Functions;
  /** Throw error if no idempotency key found (default: false) */
  throwOnNoIdempotencyKey?: boolean;
  /** Number of seconds before record expires (default: 3600) */
  expiresAfterSeconds?: number;
  /** Whether to locally cache idempotency results (default: false) */
  useLocalCache?: boolean;
  /** Number of records to keep in local cache (default: 1000) */
  maxLocalCacheSize?: number;
  /** Hash function to use (default: 'md5') */
  hashFunction?: string;
  /** AWS Lambda Context object for timeout tracking */
  lambdaContext?: Context;
  /** Hook that runs when returning idempotent response */
  responseHook?: ResponseHook;
}

DynamoDB Types

Types specific to DynamoDB persistence.

/**
 * Base options for DynamoDB persistence
 */
interface DynamoDBPersistenceOptionsBase extends BasePersistenceAttributes {
  /** DynamoDB table name (required) */
  tableName: string;
  /** Primary key attribute name (default: 'id') */
  keyAttr?: string;
  /** Sort key attribute name for composite keys */
  sortKeyAttr?: string;
  /** Static partition key value when using sort keys */
  staticPkValue?: string;
}

/**
 * DynamoDB options with client configuration
 */
interface DynamoDBPersistenceOptionsWithClientConfig
  extends DynamoDBPersistenceOptionsBase {
  /** Optional DynamoDB client configuration */
  clientConfig?: DynamoDBClientConfig;
  /** Must not be provided when using clientConfig */
  awsSdkV3Client?: never;
}

/**
 * DynamoDB options with client instance
 */
interface DynamoDBPersistenceOptionsWithClientInstance
  extends DynamoDBPersistenceOptionsBase {
  /** Optional AWS SDK v3 DynamoDBClient instance */
  awsSdkV3Client?: DynamoDBClient;
  /** Must not be provided when using awsSdkV3Client */
  clientConfig?: never;
}

/**
 * Union type for DynamoDB persistence options
 * Use either clientConfig or awsSdkV3Client, but not both
 */
type DynamoDBPersistenceOptions =
  | DynamoDBPersistenceOptionsWithClientConfig
  | DynamoDBPersistenceOptionsWithClientInstance;

Cache Types

Types specific to cache (Redis/Valkey) persistence.

/**
 * Value type for cache operations
 */
type CacheValue = string | Uint8Array<ArrayBufferLike>;

/**
 * Interface for clients compatible with Valkey and Redis-OSS operations
 */
interface CacheClient {
  /** Retrieve value associated with a key */
  get(name: string): Promise<CacheValue | null>;
  /** Set value for a key with optional parameters */
  set(
    name: CacheValue,
    value: unknown,
    options?: unknown
  ): Promise<CacheValue | null>;
  /** Delete specified keys from cache */
  del(keys: string[]): Promise<number>;
}

/**
 * Options for cache persistence layer
 */
interface CachePersistenceOptions extends BasePersistenceAttributes {
  /** Connected cache client instance (required) */
  client: CacheClient;
}

Usage Examples

Type-Safe Function Wrapper

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

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

// Type inference works correctly
const lambdaHandler = (event: any, context: Context) => {
  return { statusCode: 200 };
};

// No dataIndexArgument needed for Lambda handlers
export const handler = makeIdempotent(lambdaHandler, { persistenceStore });

// For non-handler functions, dataIndexArgument is available
const processRecord = (record: any, userId: string) => {
  // Process record
};

const processIdempotently = makeIdempotent(processRecord, {
  persistenceStore,
  dataIndexArgument: 1, // TypeScript allows this for non-handlers
});

Custom Persistence Layer

import type {
  BasePersistenceLayerInterface,
  BasePersistenceLayerOptions,
  IdempotencyRecordOptions,
} from '@aws-lambda-powertools/idempotency/types';
import { BasePersistenceLayer, IdempotencyRecord } from '@aws-lambda-powertools/idempotency/persistence';

// Implement your own persistence layer
class CustomPersistenceLayer extends BasePersistenceLayer
  implements BasePersistenceLayerInterface {

  protected async _getRecord(idempotencyKey: string): Promise<IdempotencyRecord> {
    // Your implementation
    const data = await this.customDataStore.get(idempotencyKey);
    return new IdempotencyRecord({
      idempotencyKey,
      status: data.status,
      expiryTimestamp: data.expiry,
      responseData: data.response,
    });
  }

  protected async _putRecord(record: IdempotencyRecord): Promise<void> {
    // Your implementation
    await this.customDataStore.put({
      key: record.idempotencyKey,
      status: record.getStatus(),
      expiry: record.expiryTimestamp,
    });
  }

  protected async _updateRecord(record: IdempotencyRecord): Promise<void> {
    // Your implementation
  }

  protected async _deleteRecord(record: IdempotencyRecord): Promise<void> {
    // Your implementation
  }
}

Response Hook with Types

import type { ResponseHook } from '@aws-lambda-powertools/idempotency/types';
import type { JSONValue } from '@aws-lambda-powertools/commons/types';
import type { IdempotencyRecord } from '@aws-lambda-powertools/idempotency/persistence';
import { IdempotencyConfig } from '@aws-lambda-powertools/idempotency';

const myResponseHook: ResponseHook = (
  response: JSONValue,
  record: IdempotencyRecord
): JSONValue => {
  // Type-safe response modification
  return {
    ...response,
    cached: true,
    recordStatus: record.getStatus(),
  };
};

const config = new IdempotencyConfig({
  responseHook: myResponseHook,
});

Custom Cache Client

import type { CacheClient, CacheValue } from '@aws-lambda-powertools/idempotency/cache/types';

// Implement a custom cache client
class MyCustomCacheClient implements CacheClient {
  async get(name: string): Promise<CacheValue | null> {
    // Your implementation
    return null;
  }

  async set(
    name: CacheValue,
    value: unknown,
    options?: unknown
  ): Promise<CacheValue | null> {
    // Your implementation
    return name;
  }

  async del(keys: string[]): Promise<number> {
    // Your implementation
    return keys.length;
  }
}

const customClient = new MyCustomCacheClient();
const persistenceStore = new CachePersistenceLayer({
  client: customClient,
});

Idempotency Record Creation

import type { IdempotencyRecordOptions } from '@aws-lambda-powertools/idempotency/types';
import { IdempotencyRecord } from '@aws-lambda-powertools/idempotency/persistence';

const recordOptions: IdempotencyRecordOptions = {
  idempotencyKey: 'my-function#abc123',
  status: 'COMPLETED',
  expiryTimestamp: Math.floor(Date.now() / 1000) + 3600,
  responseData: { statusCode: 200, body: 'Success' },
  payloadHash: 'def456',
};

const record = new IdempotencyRecord(recordOptions);
console.log(record.getStatus()); // 'COMPLETED'
console.log(record.getResponse()); // { statusCode: 200, body: 'Success' }

Type Relationships

BasePersistenceLayerInterface
  ↑
  |
BasePersistenceLayer (abstract)
  ↑
  |
  ├── DynamoDBPersistenceLayer
  └── CachePersistenceLayer

IdempotencyConfig
  ↓ uses
IdempotencyConfigOptions

makeIdempotent / idempotent
  ↓ uses
ItempotentFunctionOptions
  ↓ extends
IdempotencyLambdaHandlerOptions
  ↓ requires
BasePersistenceLayer

Import Paths

// Main types
import type {
  IdempotencyConfigOptions,
  IdempotencyLambdaHandlerOptions,
  ItempotentFunctionOptions,
  ResponseHook,
} from '@aws-lambda-powertools/idempotency/types';

// DynamoDB types
import type {
  DynamoDBPersistenceOptions,
  DynamoDBPersistenceOptionsBase,
  DynamoDBPersistenceOptionsWithClientConfig,
  DynamoDBPersistenceOptionsWithClientInstance,
} from '@aws-lambda-powertools/idempotency/dynamodb/types';

// Cache types
import type {
  CacheClient,
  CachePersistenceOptions,
} from '@aws-lambda-powertools/idempotency/cache/types';

// Record types
import type {
  IdempotencyRecordOptions,
  IdempotencyRecordStatusValue,
} from '@aws-lambda-powertools/idempotency/types';

// Base persistence types
import type {
  BasePersistenceAttributes,
  BasePersistenceLayerInterface,
  BasePersistenceLayerOptions,
} from '@aws-lambda-powertools/idempotency/types';

Install with Tessl CLI

npx tessl i tessl/npm-aws-lambda-powertools--idempotency

docs

cache-persistence.md

configuration.md

decorator.md

dynamodb-persistence.md

errors.md

function-wrapper.md

index.md

middleware.md

types.md

tile.json