The @idempotent decorator makes class methods idempotent. It's ideal for TypeScript class-based Lambda handlers and follows the decorator pattern.
Decorator function to make class methods idempotent by tracking executions using a persistence store.
/**
* Decorator to make class methods idempotent.
*
* Can be used on Lambda handler methods or arbitrary class methods.
* Configuration options are the same as makeIdempotent.
*
* @param options - Configuration options for idempotency behavior
* @returns Decorator function that wraps the method
*/
function idempotent(
options: ItempotentFunctionOptions<Parameters<AnyFunction>>
): (
target: unknown,
propertyKey: string,
descriptor: PropertyDescriptor
) => PropertyDescriptor;
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 with Decorator
import { idempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { LambdaInterface } from '@aws-lambda-powertools/commons';
import type { Context, APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
class MyLambdaFunction implements LambdaInterface {
@idempotent({ persistenceStore })
public async handler(
event: APIGatewayProxyEvent,
context: Context
): Promise<APIGatewayProxyResult> {
// Your business logic
return {
statusCode: 200,
body: JSON.stringify({ message: 'Success' }),
};
}
}
const handlerClass = new MyLambdaFunction();
export const handler = handlerClass.handler.bind(handlerClass);Arbitrary Method with Decorator
import { idempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { LambdaInterface } from '@aws-lambda-powertools/commons';
import type { Context, SQSEvent, SQSRecord } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
class MyHandler implements LambdaInterface {
public async handler(event: SQSEvent, context: Context): Promise<void> {
for (const record of event.Records) {
await this.processRecord(record);
}
}
@idempotent({ persistenceStore })
private async processRecord(record: SQSRecord): Promise<void> {
// Process each record idempotently
console.log('Processing:', record.body);
}
}
const handlerClass = new MyHandler();
export const handler = handlerClass.handler.bind(handlerClass);With Configuration Options
import { idempotent, IdempotencyConfig } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { LambdaInterface } from '@aws-lambda-powertools/commons';
import type { Context, APIGatewayProxyEvent } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
const config = new IdempotencyConfig({
eventKeyJmesPath: 'body',
expiresAfterSeconds: 3600,
useLocalCache: true,
});
class MyLambdaFunction implements LambdaInterface {
@idempotent({ persistenceStore, config })
public async handler(
event: APIGatewayProxyEvent,
context: Context
): Promise<void> {
// Your logic
}
}
const handlerClass = new MyLambdaFunction();
export const handler = handlerClass.handler.bind(handlerClass);With Custom Key Prefix
import { idempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { LambdaInterface } from '@aws-lambda-powertools/commons';
import type { Context, APIGatewayProxyEvent } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
class MyLambdaFunction implements LambdaInterface {
@idempotent({
persistenceStore,
keyPrefix: 'my-service',
})
public async handler(
event: APIGatewayProxyEvent,
context: Context
): Promise<void> {
// Your logic
}
}
const handlerClass = new MyLambdaFunction();
export const handler = handlerClass.handler.bind(handlerClass);Multiple Decorated Methods
import { idempotent } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
import { LambdaInterface } from '@aws-lambda-powertools/commons';
import type { Context, SQSEvent, SQSRecord } from 'aws-lambda';
const persistenceStore = new DynamoDBPersistenceLayer({
tableName: 'idempotencyTable',
});
class OrderProcessor implements LambdaInterface {
public async handler(event: SQSEvent, context: Context): Promise<void> {
for (const record of event.Records) {
const order = JSON.parse(record.body);
// Process different order types idempotently
if (order.type === 'purchase') {
await this.processPurchase(order);
} else if (order.type === 'refund') {
await this.processRefund(order);
}
}
}
@idempotent({ persistenceStore, keyPrefix: 'purchase' })
private async processPurchase(order: any): Promise<void> {
// Process purchase
}
@idempotent({ persistenceStore, keyPrefix: 'refund' })
private async processRefund(order: any): Promise<void> {
// Process refund
}
}
const processor = new OrderProcessor();
export const handler = processor.handler.bind(processor);The decorator wraps the method with the same idempotency logic as makeIdempotent:
this context of the class instancehandler.bind(handlerClass)(event, context) => Promise<T>makeIdempotentThe decorator may throw various idempotency errors:
IdempotencyKeyError - When no idempotency key can be extractedIdempotencyItemAlreadyExistsError - When a duplicate request is detectedIdempotencyAlreadyInProgressError - When a request is already being processedIdempotencyValidationError - When payload validation fails