|
1 |
| -# Powertools for AWS Lambda (TypeScript) <!-- omit in toc --> |
| 1 | +# Powertools for AWS Lambda (TypeScript) - Idempotency Utility <!-- omit in toc --> |
| 2 | + |
| 3 | + |
| 4 | +| ⚠️ **WARNING: Do not use this utility in production just yet!** ⚠️ | |
| 5 | +| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
| 6 | +| **This utility is currently released as beta developer preview** and is intended strictly for feedback and testing purposes **and not for production workloads**.. The version and all future versions tagged with the `-beta` suffix should be treated as not stable. Up until before the [General Availability release](https://github.com/awslabs/aws-lambda-powertools-typescript/milestone/10) we might introduce significant breaking changes and improvements in response to customers feedback. | _ | |
| 7 | + |
2 | 8 |
|
3 | 9 | Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#features).
|
4 | 10 |
|
5 |
| -You can use the library in both TypeScript and JavaScript code bases. |
| 11 | +## Intro |
| 12 | + |
| 13 | +This package provides a utility to implement idempotency in your Lambda functions. |
| 14 | +You can either use it as a decorator on your Lambda handler or as a wrapper on any other function. |
| 15 | +If you use middy, we also provide a middleware to make your Lambda handler idempotent. |
| 16 | +The current implementation provides a persistance layer for Amazon DynamoDB, which offers a variety of configuration options. |
| 17 | +You can also bring your own persistance layer by implementing the `IdempotencyPersistanceLayer` interface. |
| 18 | + |
| 19 | +## Key features |
| 20 | +* Prevent Lambda handler from executing more than once on the same event payload during a time window |
| 21 | +* Ensure Lambda handler returns the same result when called with the same payload |
| 22 | +* Select a subset of the event as the idempotency key using JMESPath expressions |
| 23 | +* Set a time window in which records with the same payload should be considered duplicates |
| 24 | +* Expires in-progress executions if the Lambda function times out halfway through |
| 25 | + |
| 26 | +## Usage |
| 27 | + |
| 28 | +### Decorators |
| 29 | +If you use classes to define your Lambda handlers, you can use the decorators to make your handler idempotent or a specific function idempotent. |
| 30 | +We offer two decorators: |
| 31 | +* `@idempotentLambdaHandler`: makes the handler idempotent. |
| 32 | +* `@idempotentFunction`: makes any function within your class idempotent |
6 | 33 |
|
7 |
| -[Powertools for AWS Lambda (Python)](https://github.com/awslabs/aws-lambda-powertools-python) and [Powertools for AWS Lambda (Java)](https://github.com/awslabs/aws-lambda-powertools-java) are also available. |
| 34 | +The first can only be applied to the handler function with the specific signature of a Lambda handler. |
| 35 | +The second can be applied to any function within your class. In this case you need to pass a `Record` object and provide the `dataKeywordArgument` parameter to specify the name of the argument that contains the data to be used as the idempotency key. |
| 36 | +In any of both cases yoiu need to pass the persistance layer where we will store the idempotency information. |
8 | 37 |
|
9 |
| -**[📜 Documentation](https://awslabs.github.io/aws-lambda-powertools-typescript/)** | **[NPM](https://www.npmjs.com/org/aws-lambda-powertools)** | **[Roadmap](https://github.com/awslabs/aws-lambda-powertools-roadmap/projects/1)** | **[Examples](https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/examples)** | **[Serverless TypeScript Demo](https://github.com/aws-samples/serverless-typescript-demo)** |
10 | 38 |
|
11 |
| -## Table of contents <!-- omit in toc --> |
| 39 | +### Function wrapper |
12 | 40 |
|
13 |
| -- [Features](#features) |
14 |
| -- [Getting started](#getting-started) |
15 |
| - - [Installation](#installation) |
16 |
| - - [Examples](#examples) |
17 |
| - - [Serverless TypeScript Demo application](#serverless-typescript-demo-application) |
18 |
| -- [Contribute](#contribute) |
19 |
| -- [Roadmap](#roadmap) |
20 |
| -- [Connect](#connect) |
21 |
| -- [How to support Powertools for AWS Lambda (TypeScript)?](#how-to-support-powertools-for-aws-lambda-typescript) |
22 |
| - - [Becoming a reference customer](#becoming-a-reference-customer) |
23 |
| - - [Sharing your work](#sharing-your-work) |
24 |
| - - [Using Lambda Layer](#using-lambda-layer) |
25 |
| -- [Credits](#credits) |
26 |
| -- [License](#license) |
| 41 | +A more common approach is to use the function wrapper. |
| 42 | +Similar to `@idempotentFunction` decorator you need to pass keyword argument to indicate which part of the payload will be hashed. |
27 | 43 |
|
28 |
| -## Features |
| 44 | +### Middy middleware |
| 45 | +// TODO: after e2e tests are implemented |
29 | 46 |
|
30 |
| -* **[Tracer](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions |
31 |
| -* **[Logger](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context |
32 |
| -* **[Metrics](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) |
33 |
| -* **[Parameters (beta)](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB |
| 47 | +### DynamoDB peristance layer |
| 48 | +To store the idempotency information offer a DynamoDB persistance layer. |
| 49 | +This enables you to store the hash key, payload, status for progress and expiration and much more. |
| 50 | +You can customise most of the configuration options of the DynamoDB table, i.e the names of the attributes. |
| 51 | +See the [API documentation](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/modules/.index.DynamoDBPersistenceLayer.html) for more details. |
34 | 52 |
|
35 |
| -## Getting started |
| 53 | +## Examples |
36 | 54 |
|
37 |
| -Find the complete project's [documentation here](https://awslabs.github.io/aws-lambda-powertools-typescript). |
| 55 | +### Decorator Lambda handler |
38 | 56 |
|
39 |
| -### Installation |
| 57 | +```ts |
| 58 | +import { idempotentLambdaHandler } from "@aws-lambda-powertools/idempotency"; |
| 59 | +import { DynamoDBPersistenceLayer } from "@aws-lambda-powertools/idempotency/persistance"; |
| 60 | +import type { Context } from 'aws-lambda'; |
40 | 61 |
|
41 |
| -The Powertools for AWS Lambda (TypeScript) utilities follow a modular approach, similar to the official [AWS SDK v3 for JavaScript](https://github.com/aws/aws-sdk-js-v3). |
42 |
| -Each TypeScript utility is installed as standalone NPM package. |
| 62 | +const dynamoDBPersistenceLayer = new DynamoDBPersistenceLayer(); |
43 | 63 |
|
44 |
| -Install all three core utilities at once with this single command: |
| 64 | +class MyLambdaHandler implements LambdaInterface { |
| 65 | + @idempotentLambdaHandler({ persistenceStore: dynamoDBPersistenceLayer }) |
| 66 | + public async handler(_event: any, _context: Context): Promise<string> { |
| 67 | + // your lambda code here |
| 68 | + return "Hello World"; |
| 69 | + } |
| 70 | +} |
45 | 71 |
|
46 |
| -```shell |
47 |
| -npm install @aws-lambda-powertools/logger @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics |
| 72 | +const lambdaClass = new MyLambdaHandler(); |
| 73 | +export const handler = lambdaClass.handler.bind(lambdaClass); |
48 | 74 | ```
|
49 | 75 |
|
50 |
| -Or refer to the installation guide of each utility: |
| 76 | +### Decorator function |
| 77 | + |
| 78 | +```ts |
| 79 | +import { idempotentLambdaHandler } from "@aws-lambda-powertools/idempotency"; |
| 80 | +import { DynamoDBPersistenceLayer } from "@aws-lambda-powertools/idempotency/persistance"; |
| 81 | +import type { Context } from 'aws-lambda'; |
| 82 | + |
| 83 | + |
| 84 | +const dynamoDBPersistenceLayer = new DynamoDBPersistenceLayer(); |
| 85 | + |
| 86 | +class MyLambdaHandler implements LambdaInterface { |
| 87 | + |
| 88 | + public async handler(_event: any, _context: Context): Promise<void> { |
| 89 | + for(const record of _event.Records) { |
| 90 | + await this.processRecord(record); |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + @idempotentFunction({ persistenceStore: dynamoDBPersistenceLayer, dataKeywordArgument: "payload" }) |
| 95 | + public async process(payload: Record<string, unknown>): Promise<void> { |
| 96 | + // your lambda code here |
| 97 | + } |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +The `dataKeywordArgument` parameter is optional. If not provided, the whole event will be used as the idempotency key. |
| 102 | +Otherwise, you need to specify the string name of the argument that contains the data to be used as the idempotency key. |
| 103 | +For example if you have an input like this: |
| 104 | + |
51 | 105 |
|
52 |
| -👉 [Installation guide for the **Tracer** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer#getting-started) |
| 106 | +```json |
| 107 | +{ |
| 108 | + "transactionId": 1235, |
| 109 | + "product": "book", |
| 110 | + "quantity": 1, |
| 111 | + "price": 10 |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +You can use `transactionId` as the idempotency key. This will ensure that the same transaction is not processed twice. |
53 | 116 |
|
54 |
| -👉 [Installation guide for the **Logger** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/logger#getting-started) |
| 117 | +### Function wrapper |
55 | 118 |
|
56 |
| -👉 [Installation guide for the **Metrics** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/metrics#getting-started) |
| 119 | +In case where you don't use classes and decorators you can wrap your function to make it idempotent. |
57 | 120 |
|
58 |
| -👉 [Installation guide for the **Parameters** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/utilities/parameters/#getting-started) |
| 121 | +```ts |
| 122 | +import { makeFunctionIdempotent } from "@aws-lambda-powertools/idempotency"; |
| 123 | +import { DynamoDBPersistenceLayer } from "@aws-lambda-powertools/idempotency/persistance"; |
| 124 | +import type { Context } from 'aws-lambda'; |
59 | 125 |
|
60 |
| -### Examples |
61 | 126 |
|
62 |
| -* [CDK](https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/examples/cdk) |
63 |
| -* [SAM](https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/examples/sam) |
| 127 | +const dynamoDBPersistenceLayer = new DynamoDBPersistenceLayer(); |
| 128 | +const processingFunction = async (payload: Record<string, unknown>): Promise<void> => { |
| 129 | + // your lambda code here |
| 130 | +}; |
64 | 131 |
|
65 |
| -### Serverless TypeScript Demo application |
| 132 | +const processIdempotently = makeFunctionIdempotent(proccessingFunction, { |
| 133 | + persistenceStore: dynamoDBPersistenceLayer, |
| 134 | + dataKeywordArgument: "transactionId" |
| 135 | +}); |
66 | 136 |
|
67 |
| -The [Serverless TypeScript Demo](https://github.com/aws-samples/serverless-typescript-demo) shows how to use Powertools for AWS Lambda (TypeScript). |
68 |
| -You can find instructions on how to deploy and load test this application in the [repository](https://github.com/aws-samples/serverless-typescript-demo). |
| 137 | +export const handler = async ( |
| 138 | + _event: any, |
| 139 | + _context: Context |
| 140 | +): Promise<void> => { |
| 141 | + for (const record of _event.Records) { |
| 142 | + await processIdempotently(record); |
| 143 | + } |
| 144 | +}; |
| 145 | +``` |
69 | 146 |
|
70 | 147 | ## Contribute
|
71 | 148 |
|
|
0 commit comments