The makeIdempotent function is a higher-order function that wraps any function to make it idempotent. It works with both Lambda handlers and arbitrary functions.
Wraps a function to make it idempotent by tracking executions using a persistence store.
/**
* Function wrapper to make any function idempotent.
*
* By default, the entire first argument is hashed to create the idempotency key.
* Use eventKeyJmesPath to hash only a subset of the payload, or dataIndexArgument
* to hash a different function argument.
*
* @param fn - The function to make idempotent
* @param options - Configuration options for idempotency behavior
* @returns A new function with idempotency behavior
*/
function makeIdempotent<Func extends AnyFunction>(
fn: Func,
options: ItempotentFunctionOptions<Parameters<Func>>
): (...args: Parameters<Func>) => ReturnType<Func>;
type AnyFunction = (...args: Array<any>) => any;
type ItempotentFunctionOptions<T extends Array<any>> = T[1] extends Context
? IdempotencyLambdaHandlerOptions
: IdempotencyLambdaHandlerOptions & {
dataIndexArgument?: number;
};
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;
}Usage Examples:
Lambda Handler Idempotency
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import type { Context, APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
const myHandler = async (
event: APIGatewayProxyEvent,
context: Context
): Promise<APIGatewayProxyResult> => {
// Your business logic
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success' }),
};
};
// Wrap the handler to make it idempotent
// The event object (first argument) is automatically used as the idempotency payload
export const handler = makeIdempotent(myHandler, { persistenceStore });Arbitrary Function Idempotency
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import type { SQSEvent, SQSRecord } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
// Make an arbitrary function idempotent
const processRecord = async (record: SQSRecord): Promise<void> => {
// Process the record
console.log('Processing:', record.body);
};
const processIdempotently = makeIdempotent(processRecord, {
persistenceStore,
});
export const handler = async (event: SQSEvent) => {
for (const record of event.Records) {
await processIdempotently(record);
}
};Using Subset of Payload with JMESPath
import { makeIdempotent, IdempotencyConfig } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import type { Context, APIGatewayProxyEvent } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
const myHandler = async (
event: APIGatewayProxyEvent,
context: Context
): Promise<void> => {
// Your logic
};
// Use only the 'user' field from requestContext.identity for idempotency key
export const handler = makeIdempotent(myHandler, {
persistenceStore,
config: new IdempotencyConfig({
eventKeyJmesPath: 'requestContext.identity.user',
}),
});Using JMESPath Built-in Functions
import { makeIdempotent, IdempotencyConfig } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import type { Context, APIGatewayProxyEvent } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
const myHandler = async (
event: APIGatewayProxyEvent,
context: Context
): Promise<void> => {
// Your logic
};
// Use powertools_json() to decode JSON body and extract specific fields
export const handler = makeIdempotent(myHandler, {
persistenceStore,
config: new IdempotencyConfig({
eventKeyJmesPath: 'powertools_json(body).["user", "productId"]',
}),
});Multi-Parameter Functions with dataIndexArgument
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import type { Context, SQSEvent, SQSRecord } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
const processRecord = async (
record: SQSRecord,
customerId: string
): Promise<void> => {
// Your processing logic
console.log('Processing for customer:', customerId);
};
// Use the second argument (customerId) as the idempotency key
const processIdempotently = makeIdempotent(processRecord, {
persistenceStore,
dataIndexArgument: 1, // Index is zero-based, so 1 means second argument
});
export const handler = async (event: SQSEvent, context: Context) => {
for (const record of event.Records) {
await processIdempotently(record, 'customer-123');
}
};With Custom Key Prefix
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import type { Context, APIGatewayProxyEvent } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
const myHandler = async (
event: APIGatewayProxyEvent,
context: Context
): Promise<void> => {
// Your logic
};
// Add a custom prefix to idempotency keys
export const handler = makeIdempotent(myHandler, {
persistenceStore,
keyPrefix: 'my-custom-prefix',
});The function may throw various idempotency errors:
IdempotencyKeyError - When no idempotency key can be extracted and throwOnNoIdempotencyKey is trueIdempotencyItemAlreadyExistsError - When a duplicate request is detectedIdempotencyAlreadyInProgressError - When a request is already being processedIdempotencyValidationError - When payload validation fails