Skip to content

Commit 3b5ccc1

Browse files
authored
docs(idempotency): write utility docs (#1592)
* docs: base docs * wip * chore: added paths to snippets tsconfig * chore: added page to docs menu * docs(idempotency): utility docs * highlights * chore: remove CDK mention
1 parent 9f1696b commit 3b5ccc1

22 files changed

+1437
-2
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ coverage
3333

3434
# Python virtual environments (for running mkdocs locally)
3535
venv
36+
.venv
3637

3738
# Static documentation site generated by Mkdocs
3839
site
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { makeHandlerIdempotent } from '@aws-lambda-powertools/idempotency/middleware';
2+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
3+
import middy from '@middy/core';
4+
import type { Context } from 'aws-lambda';
5+
import type { Request, Response } from './types';
6+
7+
const persistenceStore = new DynamoDBPersistenceLayer({
8+
tableName: 'idempotencyTableName',
9+
keyAttr: 'idempotencyKey',
10+
expiryAttr: 'expiresAt',
11+
inProgressExpiryAttr: 'inProgressExpiresAt',
12+
statusAttr: 'currentStatus',
13+
dataAttr: 'resultData',
14+
validationKeyAttr: 'validationKey',
15+
});
16+
17+
export const handler = middy(
18+
async (_event: Request, _context: Context): Promise<Response> => {
19+
try {
20+
// ... create payment
21+
22+
return {
23+
paymentId: '1234567890',
24+
message: 'success',
25+
statusCode: 200,
26+
};
27+
} catch (error) {
28+
throw new Error('Error creating payment');
29+
}
30+
}
31+
).use(
32+
makeHandlerIdempotent({
33+
persistenceStore,
34+
})
35+
);

Diff for: docs/snippets/idempotency/makeHandlerIdempotent.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { randomUUID } from 'node:crypto';
2+
import { makeHandlerIdempotent } from '@aws-lambda-powertools/idempotency/middleware';
3+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
4+
import middy from '@middy/core';
5+
import type { Context } from 'aws-lambda';
6+
import type { Request, Response, SubscriptionResult } from './types';
7+
8+
const persistenceStore = new DynamoDBPersistenceLayer({
9+
tableName: 'idempotencyTableName',
10+
});
11+
12+
const createSubscriptionPayment = async (
13+
event: Request
14+
): Promise<SubscriptionResult> => {
15+
// ... create payment
16+
return {
17+
id: randomUUID(),
18+
productId: event.productId,
19+
};
20+
};
21+
22+
export const handler = middy(
23+
async (event: Request, _context: Context): Promise<Response> => {
24+
try {
25+
const payment = await createSubscriptionPayment(event);
26+
27+
return {
28+
paymentId: payment.id,
29+
message: 'success',
30+
statusCode: 200,
31+
};
32+
} catch (error) {
33+
throw new Error('Error creating payment');
34+
}
35+
}
36+
).use(
37+
makeHandlerIdempotent({
38+
persistenceStore,
39+
})
40+
);
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { randomUUID } from 'node:crypto';
2+
import {
3+
makeIdempotent,
4+
IdempotencyConfig,
5+
} from '@aws-lambda-powertools/idempotency';
6+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
7+
import type { Context } from 'aws-lambda';
8+
import type { Request, Response, SubscriptionResult } from './types';
9+
10+
const persistenceStore = new DynamoDBPersistenceLayer({
11+
tableName: 'idempotencyTableName',
12+
});
13+
const config = new IdempotencyConfig({});
14+
15+
const reportSubscriptionMetrics = async (
16+
_transactionId: string,
17+
_user: string
18+
): Promise<void> => {
19+
// ... send notification
20+
};
21+
22+
const createSubscriptionPayment = makeIdempotent(
23+
async (
24+
transactionId: string,
25+
event: Request
26+
): Promise<SubscriptionResult> => {
27+
// ... create payment
28+
return {
29+
id: transactionId,
30+
productId: event.productId,
31+
};
32+
},
33+
{
34+
persistenceStore,
35+
dataIndexArgument: 1,
36+
config,
37+
}
38+
);
39+
40+
export const handler = async (
41+
event: Request,
42+
context: Context
43+
): Promise<Response> => {
44+
config.registerLambdaContext(context);
45+
try {
46+
const transactionId = randomUUID();
47+
const payment = await createSubscriptionPayment(transactionId, event);
48+
49+
await reportSubscriptionMetrics(transactionId, event.user);
50+
51+
return {
52+
paymentId: payment.id,
53+
message: 'success',
54+
statusCode: 200,
55+
};
56+
} catch (error) {
57+
throw new Error('Error creating payment');
58+
}
59+
};

Diff for: docs/snippets/idempotency/makeIdempotentBase.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { randomUUID } from 'node:crypto';
2+
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
3+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
4+
import type { Context } from 'aws-lambda';
5+
import type { Request, Response, SubscriptionResult } from './types';
6+
7+
const persistenceStore = new DynamoDBPersistenceLayer({
8+
tableName: 'idempotencyTableName',
9+
});
10+
11+
const createSubscriptionPayment = async (
12+
event: Request
13+
): Promise<SubscriptionResult> => {
14+
// ... create payment
15+
return {
16+
id: randomUUID(),
17+
productId: event.productId,
18+
};
19+
};
20+
21+
export const handler = makeIdempotent(
22+
async (event: Request, _context: Context): Promise<Response> => {
23+
try {
24+
const payment = await createSubscriptionPayment(event);
25+
26+
return {
27+
paymentId: payment.id,
28+
message: 'success',
29+
statusCode: 200,
30+
};
31+
} catch (error) {
32+
throw new Error('Error creating payment');
33+
}
34+
},
35+
{
36+
persistenceStore,
37+
}
38+
);

Diff for: docs/snippets/idempotency/makeIdempotentJmes.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { randomUUID } from 'node:crypto';
2+
import {
3+
makeIdempotent,
4+
IdempotencyConfig,
5+
} from '@aws-lambda-powertools/idempotency';
6+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
7+
import type { Context } from 'aws-lambda';
8+
import type { Request, Response, SubscriptionResult } from './types';
9+
10+
const persistenceStore = new DynamoDBPersistenceLayer({
11+
tableName: 'idempotencyTableName',
12+
});
13+
14+
const createSubscriptionPayment = async (
15+
user: string,
16+
productId: string
17+
): Promise<SubscriptionResult> => {
18+
// ... create payment
19+
return {
20+
id: randomUUID(),
21+
productId: productId,
22+
};
23+
};
24+
25+
// Extract the idempotency key from the request headers
26+
const config = new IdempotencyConfig({
27+
eventKeyJmesPath: 'headers."X-Idempotency-Key"',
28+
});
29+
30+
export const handler = makeIdempotent(
31+
async (event: Request, _context: Context): Promise<Response> => {
32+
try {
33+
const payment = await createSubscriptionPayment(
34+
event.user,
35+
event.productId
36+
);
37+
38+
return {
39+
paymentId: payment.id,
40+
message: 'success',
41+
statusCode: 200,
42+
};
43+
} catch (error) {
44+
throw new Error('Error creating payment');
45+
}
46+
},
47+
{
48+
persistenceStore,
49+
config,
50+
}
51+
);
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { randomUUID } from 'node:crypto';
2+
import {
3+
makeIdempotent,
4+
IdempotencyConfig,
5+
} from '@aws-lambda-powertools/idempotency';
6+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
7+
import type { Context } from 'aws-lambda';
8+
import type { Request, Response, SubscriptionResult } from './types';
9+
10+
const persistenceStore = new DynamoDBPersistenceLayer({
11+
tableName: 'idempotencyTableName',
12+
});
13+
const config = new IdempotencyConfig({});
14+
15+
const createSubscriptionPayment = makeIdempotent(
16+
async (
17+
transactionId: string,
18+
event: Request
19+
): Promise<SubscriptionResult> => {
20+
// ... create payment
21+
return {
22+
id: transactionId,
23+
productId: event.productId,
24+
};
25+
},
26+
{
27+
persistenceStore,
28+
dataIndexArgument: 1,
29+
config,
30+
}
31+
);
32+
33+
export const handler = async (
34+
event: Request,
35+
context: Context
36+
): Promise<Response> => {
37+
// Register the Lambda context to the IdempotencyConfig instance
38+
config.registerLambdaContext(context);
39+
try {
40+
const transactionId = randomUUID();
41+
const payment = await createSubscriptionPayment(transactionId, event);
42+
43+
return {
44+
paymentId: payment.id,
45+
message: 'success',
46+
statusCode: 200,
47+
};
48+
} catch (error) {
49+
throw new Error('Error creating payment');
50+
}
51+
};

Diff for: docs/snippets/idempotency/requiredIdempotencyKey.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {
2+
IdempotencyConfig,
3+
makeIdempotent,
4+
} from '@aws-lambda-powertools/idempotency';
5+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
6+
7+
const persistenceStore = new DynamoDBPersistenceLayer({
8+
tableName: 'IdempotencyTable',
9+
});
10+
11+
// Requires "user"."uid" and "orderId" to be present
12+
const config = new IdempotencyConfig({
13+
eventKeyJmesPath: '[user.uid, orderId]',
14+
throwOnNoIdempotencyKey: true,
15+
});
16+
17+
export const handler = makeIdempotent((_event: unknown) => ({}), {
18+
persistenceStore,
19+
config,
20+
});

Diff for: docs/snippets/idempotency/types.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
type Request = {
2+
user: string;
3+
productId: string;
4+
};
5+
6+
type Response = {
7+
[key: string]: unknown;
8+
};
9+
10+
type SubscriptionResult = {
11+
id: string;
12+
productId: string;
13+
};
14+
15+
export { Request, Response, SubscriptionResult };

Diff for: docs/snippets/idempotency/workingWithCompositeKey.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { makeHandlerIdempotent } from '@aws-lambda-powertools/idempotency/middleware';
2+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
3+
import middy from '@middy/core';
4+
import type { Context } from 'aws-lambda';
5+
import type { Request, Response } from './types';
6+
7+
const persistenceStore = new DynamoDBPersistenceLayer({
8+
tableName: 'idempotencyTableName',
9+
sortKeyAttr: 'sort_key',
10+
});
11+
12+
export const handler = middy(
13+
async (_event: Request, _context: Context): Promise<Response> => {
14+
try {
15+
// ... create payment
16+
17+
return {
18+
paymentId: '12345',
19+
message: 'success',
20+
statusCode: 200,
21+
};
22+
} catch (error) {
23+
throw new Error('Error creating payment');
24+
}
25+
}
26+
).use(
27+
makeHandlerIdempotent({
28+
persistenceStore,
29+
})
30+
);

Diff for: docs/snippets/idempotency/workingWithCustomClient.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
2+
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';
3+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
4+
import type { Context } from 'aws-lambda';
5+
import type { Request, Response } from './types';
6+
7+
const customDynamoDBClient = new DynamoDBClient({
8+
endpoint: 'http://localhost:8000',
9+
});
10+
const persistenceStore = new DynamoDBPersistenceLayer({
11+
tableName: 'idempotencyTableName',
12+
awsSdkV3Client: customDynamoDBClient,
13+
});
14+
15+
export const handler = makeIdempotent(
16+
async (_event: Request, _context: Context): Promise<Response> => {
17+
try {
18+
// ... create payment
19+
20+
return {
21+
paymentId: '12345',
22+
message: 'success',
23+
statusCode: 200,
24+
};
25+
} catch (error) {
26+
throw new Error('Error creating payment');
27+
}
28+
},
29+
{
30+
persistenceStore,
31+
}
32+
);

0 commit comments

Comments
 (0)