From 6727ad19343f54a71dc80f7b3e84e3d16ab81b40 Mon Sep 17 00:00:00 2001 From: Zelys Date: Mon, 6 Apr 2026 18:13:09 -0500 Subject: [PATCH 1/2] docs: separate API reference documentation from README customer section Creates dedicated API_README.md for each package without the 'Becoming a reference customer' section. TypeDoc now references API_README.md while README.md (published to npm) retains customer references, aligning with AWS documentation guidelines and providing consistent API reference experience. Fixes #4594 --- packages/batch/API_README.md | 160 +++++++++++++ packages/batch/typedoc.json | 2 +- packages/commons/API_README.md | 99 ++++++++ packages/commons/typedoc.json | 2 +- packages/event-handler/API_README.md | 312 ++++++++++++++++++++++++++ packages/event-handler/typedoc.json | 2 +- packages/idempotency/API_README.md | 324 +++++++++++++++++++++++++++ packages/idempotency/typedoc.json | 2 +- packages/jmespath/API_README.md | 185 +++++++++++++++ packages/jmespath/typedoc.json | 2 +- packages/kafka/API_README.md | 265 ++++++++++++++++++++++ packages/kafka/typedoc.json | 2 +- packages/logger/API_README.md | 219 ++++++++++++++++++ packages/logger/typedoc.json | 2 +- packages/metrics/API_README.md | 144 ++++++++++++ packages/metrics/typedoc.json | 2 +- packages/parameters/API_README.md | 209 +++++++++++++++++ packages/parameters/typedoc.json | 2 +- packages/parser/API_README.md | 314 ++++++++++++++++++++++++++ packages/parser/typedoc.json | 2 +- packages/tracer/API_README.md | 130 +++++++++++ packages/tracer/typedoc.json | 2 +- packages/validation/API_README.md | 242 ++++++++++++++++++++ packages/validation/typedoc.json | 2 +- 24 files changed, 2615 insertions(+), 12 deletions(-) create mode 100644 packages/batch/API_README.md create mode 100644 packages/commons/API_README.md create mode 100644 packages/event-handler/API_README.md create mode 100644 packages/idempotency/API_README.md create mode 100644 packages/jmespath/API_README.md create mode 100644 packages/kafka/API_README.md create mode 100644 packages/logger/API_README.md create mode 100644 packages/metrics/API_README.md create mode 100644 packages/parameters/API_README.md create mode 100644 packages/parser/API_README.md create mode 100644 packages/tracer/API_README.md create mode 100644 packages/validation/API_README.md diff --git a/packages/batch/API_README.md b/packages/batch/API_README.md new file mode 100644 index 0000000000..a01f8077a7 --- /dev/null +++ b/packages/batch/API_README.md @@ -0,0 +1,160 @@ +# Powertools for AWS Lambda (TypeScript) - Batch Processing Utility + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the library in both TypeScript and JavaScript code bases. + +## Intro + +The Batch Processing utility handles partial failures when processing batches from Amazon SQS, Amazon Kinesis Data Streams, and Amazon DynamoDB Streams. + +## Usage + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/batch +``` + +### Batch Processor + +When using SQS, Kinesis Data Streams, or DynamoDB Streams as a Lambda event source, your Lambda functions are triggered with a batch of messages. + +If your function fails to process any message from the batch, the entire batch returns to your queue or stream. This same batch is then retried until either condition happens first: **a)** your Lambda function returns a successful response, **b)** record reaches maximum retry attempts, or **c)** when records expire. + +With this utility, batch records are processed individually – only messages that failed to be processed return to the queue or stream for a further retry. + +### SQS Processor + +When using SQS as a Lambda event source, you can specify the `EventType.SQS` to process the records. The response will be a `SQSBatchResponse` which contains a list of items that failed to be processed. + +```ts +import { + BatchProcessorSync, + EventType, + processPartialResponseSync, +} from '@aws-lambda-powertools/batch'; +import { Logger } from '@aws-lambda-powertools/logger'; +import type { SQSHandler, SQSRecord } from 'aws-lambda'; + +const processor = new BatchProcessorSync(EventType.SQS); +const logger = new Logger(); + +const recordHandler = (record: SQSRecord): void => { + const payload = record.body; + if (payload) { + const item = JSON.parse(payload); + logger.info('Processed item', { item }); + } +}; + +export const handler: SQSHandler = async (event, context) => + processPartialResponseSync(event, recordHandler, processor, { + context, + }); +``` + +### Kinesis Processor + +When using Kinesis Data Streams as a Lambda event source, you can specify the `EventType.KinesisDataStreams` to process the records. The response will be a `KinesisStreamBatchResponse` which contains a list of items that failed to be processed. + +```ts +import { + BatchProcessorSync, + EventType, + processPartialResponseSync, +} from '@aws-lambda-powertools/batch'; +import { Logger } from '@aws-lambda-powertools/logger'; +import type { KinesisStreamHandler, KinesisStreamRecord } from 'aws-lambda'; + +const processor = new BatchProcessorSync(EventType.KinesisDataStreams); +const logger = new Logger(); + +const recordHandler = (record: KinesisStreamRecord): void => { + logger.info('Processing record', { record: record.kinesis.data }); + const payload = JSON.parse(record.kinesis.data); + logger.info('Processed item', { item: payload }); +}; + +export const handler: KinesisStreamHandler = async (event, context) => + processPartialResponseSync(event, recordHandler, processor, { + context, + }); +``` + +### DynamoDB Streams Processor + +When using DynamoDB Streams as a Lambda event source, you can use the `BatchProcessorSync` with the `EventType.DynamoDBStreams` to process the records. The response will be a `DynamoDBBatchResponse` which contains a list of items that failed to be processed. + +```ts +import { + BatchProcessor, + EventType, + processPartialResponseSync, +} from '@aws-lambda-powertools/batch'; +import { Logger } from '@aws-lambda-powertools/logger'; +import type { DynamoDBRecord, DynamoDBStreamHandler } from 'aws-lambda'; + +const processor = new BatchProcessor(EventType.DynamoDBStreams); // (1)! +const logger = new Logger(); + +const recordHandler = (record: DynamoDBRecord): void => { + if (record.dynamodb && record.dynamodb.NewImage) { + logger.info('Processing record', { record: record.dynamodb.NewImage }); + const message = record.dynamodb.NewImage.Message.S; + if (message) { + const payload = JSON.parse(message); + logger.info('Processed item', { item: payload }); + } + } +}; + +export const handler: DynamoDBStreamHandler = async (event, context) => + processPartialResponseSync(event, recordHandler, processor, { + context, + }); +``` + +### Async processing + +If your use case allows you to process multiple records at the same time without conflicting with each other, you can use the `BatchProcessor` to process records asynchronously. This will create an array of promises that will be resolved once all records have been processed. + +```ts +import { + BatchProcessor, + EventType, + processPartialResponse, +} from '@aws-lambda-powertools/batch'; +import type { SQSHandler, SQSRecord } from 'aws-lambda'; + +const processor = new BatchProcessor(EventType.SQS); + +const recordHandler = async (record: SQSRecord): Promise => { + const res = await fetch('https://httpbin.org/anything', { + body: JSON.stringify({ message: record.body }), + }); + + return res.status; +}; + +export const handler: SQSHandler = async (event, context) => + await processPartialResponse(event, recordHandler, processor, { + context, + }); +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/batch/) for more examples. + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/batch/typedoc.json b/packages/batch/typedoc.json index 5873dab1cd..eb41ee0204 100644 --- a/packages/batch/typedoc.json +++ b/packages/batch/typedoc.json @@ -1,5 +1,5 @@ { "extends": ["../../typedoc.base.json"], "entryPoints": ["./src/index.ts", "./src/parser.ts", "./src/types.ts"], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/commons/API_README.md b/packages/commons/API_README.md new file mode 100644 index 0000000000..51e3a5d910 --- /dev/null +++ b/packages/commons/API_README.md @@ -0,0 +1,99 @@ +# Powertools for AWS Lambda (TypeScript) + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the library in both TypeScript and JavaScript code bases. + +## Intro + +The Commons package contains a set of utilities that are shared across one or more Powertools for AWS Lambda (TypeScript) utilities. Some of these utilities can also be used independently in your AWS Lambda functions. + +## Usage + +To get started, install the utility by running: + +```sh +npm i @aws-lambda-powertools/commons +``` + +### Type utils + +When working with different objects and values, you may want to do runtime type checks. The utility comes with a set of type utilities that you can use to check the type of an object or value. + +```typescript +import { isRecord } from '@aws-lambda-powertools/commons/typeUtils'; +import { isString } from '@aws-lambda-powertools/commons/typeUtils'; +import { isTruthy } from '@aws-lambda-powertools/commons/typeUtils'; + + +const value = { key: 'value' }; +if (isRecord(value)) { + // value is a record +} + +const stringValue = 'string'; +if (isString(stringValue)) { + // stringValue is a string +} + +const truthyValue = 'true'; +if (isTruthy(truthyValue)) { + // truthyValue is truthy +} +``` + +You can find a full list of type utilities available [in the API docs](https://docs.aws.amazon.com/powertools/typescript/latest/api/modules/_aws-lambda-powertools_commons.typeUtils.html). Many of these utilities also double as type guards, which you can use to narrow down the type of an object or value. + +### Base64 utils + +When working with Base64-encoded data, you can use the `fromBase64` utilities to quickly decode data and convert it to a `Uint8Array`. + +```typescript + +import { fromBase64 } from '@aws-lambda-powertools/commons/utils/base64'; + +const encodedValue = 'aGVsbG8gd29ybGQ='; + +const decoded = fromBase64(encodedValue); +// new Uint8Array([ 97, 71, 86, 115, 98, 71, 56, 103, 100, 50, 57, 121, 98, 71, 81, 61 ]); +``` + +### JSON type utils + +In some cases, you may want to define a type for a JSON object or value. The utility comes with a set of types that you can use to define your JSON objects. + +```typescript +import type { JSONValue, JSONObject, JSONArray } from '@aws-lambda-powertools/commons'; +``` + +### Lambda interface + +When using object-oriented patterns to define your Lambda handlers, you can use the `LambdaHandler` interface to define the shape of your handler methods. + +```typescript +import type { Context } from 'aws-lambda'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; + +class Lambda implements LambdaInterface { + public handler = async (event: unknown, context: Context) => { + // Your handler code here + } +} + +const handlerClass = new Lambda(); +export const handler = lambda.handler.bind(lambda); +``` + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/commons/typedoc.json b/packages/commons/typedoc.json index e85c329a9d..521f5facd8 100644 --- a/packages/commons/typedoc.json +++ b/packages/commons/typedoc.json @@ -9,5 +9,5 @@ "./src/fromBase64.ts", "./src/LRUCache.ts" ], - "readme": "./README.md" + "readme": "./API_README.md" } diff --git a/packages/event-handler/API_README.md b/packages/event-handler/API_README.md new file mode 100644 index 0000000000..6dc0b8f75a --- /dev/null +++ b/packages/event-handler/API_README.md @@ -0,0 +1,312 @@ +# Powertools for AWS Lambda (TypeScript) - Event Handler Utility + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the library in both TypeScript and JavaScript code bases. + +## Intro + +Event handler for AWS AppSync GraphQL APIs, AWS AppSync Events APIs, and Amazon Bedrock Agent Functions. + +## Usage + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/event-handler +``` + +## AppSync Events + +Event Handler for AWS AppSync real-time events. + +* Easily handle publish and subscribe events with dedicated handler methods +* Automatic routing based on namespace and channel patterns +* Support for wildcard patterns to create catch-all handlers +* Process events in parallel corontrol aggregation for batch processing +* Graceful error handling for individual events + +### Handle publish events + +When using the publish event handler, you can register a handler for a specific channel or a wildcard pattern. The handler will be called once for each message received on that channel. + +```typescript +import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events'; + +const app = new AppSyncEventsResolver(); + +app.onPublish('/default/foo', async (payload) => { + // your logic here + return payload; +}); + +export const handler = async (event, context) => + app.resolve(event, context); +``` + +In some cases, you might want to process all the messages at once, for example to optimize downstream operations. In this case, you can set the `aggregate` option to `true` when registering the handler. This will cause the handler to be called once for all messages received on that channel. + +```typescript +import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events'; + +const app = new AppSyncEventsResolver(); + +app.onPublish('/default/foo', async (payloads) => { + const newMessages = []; + for (const message of payloads) { + // your logic here + } + + return newMessages; +}, { + aggregate: true +}); + +export const handler = async (event, context) => + app.resolve(event, context); +``` + +### Handle subscribe events + +You can also register a handler for subscribe events. This handler will be called once for each subscription request received on the specified channel. You can use this handler to perform any necessary setup or validation before allowing the subscription to proceed. + +```typescript +import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events'; + +const app = new AppSyncEventsResolver(); + +app.onSubscribe('/default/foo', async (event) => { + // your logic here +}); + +export const handler = async (event, context) => + app.resolve(event, context); +``` + +If you want to reject a subscription request, you can throw an `UnauthorizedException` error. This will cause the subscription to be rejected and the client will receive an error message. + +```typescript +import { + AppSyncEventsResolver, + UnauthorizedException, +} from '@aws-lambda-powertools/event-handler/appsync-events'; + +const app = new AppSyncEventsResolver(); + +app.onSubscribe('/default/foo', async (event) => { + // your logic here + throw new UnauthorizedException('Unauthorized'); +}); + +export const handler = async (event, context) => + app.resolve(event, context); +``` + +## AppSync GraphQL + +The Event Handler for AWS AppSync GraphQL APIs allows you to easily handle GraphQL requests in your Lambda functions. It enables you to define resolvers for GraphQL types and fields, making it easier to handle GraphQL requests without the need for complex VTL or JavaScript templates. + +* Route events based on GraphQL type and field keys +* Automatically parse API arguments to function parameters +* Handle GraphQL responses and errors in the expected format + +### Handle query requests + +When registering a resolver for a Query type, you can use the `onQuery()` method. This method allows you to define a function that will be invoked when a GraphQL Query is made. + +```typescript +import { Logger } from '@aws-lambda-powertools/logger'; +import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql'; +import type { Context } from 'aws-lambda'; + +const logger = new Logger({ + serviceName: 'TodoManager', +}); +const app = new AppSyncGraphQLResolver({ logger }); + +app.onQuery<{ id: string }>('getTodo', async ({ id }) => { + logger.debug('Resolving todo', { id }); + // Simulate fetching a todo from a database or external service + return { + id, + title: 'Todo Title', + completed: false, + }; +}); + +export const handler = async (event: unknown, context: Context) => + app.resolve(event, context); +``` + +### Handle mutation requests + +Similarly, you can register a resolver for a Mutation type using the `onMutation()` method. This method allows you to define a function that will be invoked when a GraphQL Mutation is made. + +```typescript +import { Logger } from '@aws-lambda-powertools/logger'; +import { + AppSyncGraphQLResolver, + makeId, +} from '@aws-lambda-powertools/event-handler/appsync-graphql'; +import type { Context } from 'aws-lambda'; + +const logger = new Logger({ + serviceName: 'TodoManager', +}); +const app = new AppSyncGraphQLResolver({ logger }); + +app.onMutation<{ title: string }>('createTodo', async ({ title }) => { + logger.debug('Creating todo', { title }); + const todoId = makeId(); + // Simulate creating a todo in a database or external service + return { + id: todoId, + title, + completed: false, + }; +}); + +export const handler = async (event: unknown, context: Context) => + app.resolve(event, context); +``` + +### Generic resolver + +When you want to have more control over the type and field, you can use the `resolver()` method. This method allows you to register a function for a specific GraphQL type and field including custom types. + +```typescript +import { Logger } from '@aws-lambda-powertools/logger'; +import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql'; +import type { Context } from 'aws-lambda'; + +const logger = new Logger({ + serviceName: 'TodoManager', +}); +const app = new AppSyncGraphQLResolver({ logger }); + +app.resolver( + async () => { + logger.debug('Resolving todos'); + // Simulate fetching a todo from a database or external service + return [ + { + id: 'todo-id', + title: 'Todo Title', + completed: false, + }, + { + id: 'todo-id-2', + title: 'Todo Title 2', + completed: true, + }, + ]; + }, + { + fieldName: 'listTodos', + typeName: 'Query', + } +); + +export const handler = async (event: unknown, context: Context) => + app.resolve(event, context); +``` + +See the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-events) for more details on how to use the AppSync event handler. + +## Bedrock Agent Functions + +Event Handler for Amazon Bedrock Agent Functions. + +* Easily expose tools for your Large Language Model (LLM) agents +* Automatic routing based on tool name and function details +* Graceful error handling and response formatting + +### Handle tool use + +When using the Bedrock Agent Functions event handler, you can register a handler for a specific tool name. The handler will be called when the agent uses that tool. + +```typescript +import { BedrockAgentFunctionResolver } from '@aws-lambda-powertools/event-handler/bedrock-agent'; +import type { Context } from 'aws-lambda'; + +const app = new BedrockAgentFunctionResolver(); + +app.tool<{ city: string }>( + async ({ city }) => { + // Simulate fetching weather data for the city + return { + city, + temperature: '20°C', + condition: 'Sunny', + }; + }, + { + name: 'getWeatherForCity', + description: 'Get weather for a specific city', // (1)! + } +); + +export const handler = async (event: unknown, context: Context) => + app.resolve(event, context); +``` + +You can also work with session attributes, which are key-value pairs that can be used to store information about the current session. The session attributes are automatically passed to the handler and can be used to store information that needs to be persisted across multiple tool invocations. + +```typescript +import { + BedrockAgentFunctionResolver, + BedrockFunctionResponse, +} from '@aws-lambda-powertools/event-handler/bedrock-agent'; +import type { Context } from 'aws-lambda'; + +const app = new BedrockAgentFunctionResolver(); + +app.tool<{ city: string }>( + async ({ city }, { event }) => { + const { + sessionAttributes, + promptSessionAttributes, + knowledgeBasesConfiguration, + } = event; + + // your logic to fetch weather data for the city + + return new BedrockFunctionResponse({ + body: JSON.stringify({ + city, + temperature: '20°C', + condition: 'Sunny', + }), + sessionAttributes: { + ...sessionAttributes, + isGoodWeather: true, + }, + promptSessionAttributes, + knowledgeBasesConfiguration, + }); + }, + { + name: 'getWeatherForCity', + description: 'Get weather for a specific city', + } +); + +export const handler = async (event: unknown, context: Context) => + app.resolve(event, context); +``` + +See the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-events) for more details on how to use the AppSync event handler. + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +* **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +* **Email**: diff --git a/packages/event-handler/typedoc.json b/packages/event-handler/typedoc.json index 4a6e4113b0..c8a382be3c 100644 --- a/packages/event-handler/typedoc.json +++ b/packages/event-handler/typedoc.json @@ -7,5 +7,5 @@ "./src/http/index.ts", "./src/types/index.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/idempotency/API_README.md b/packages/idempotency/API_README.md new file mode 100644 index 0000000000..5d49cbc890 --- /dev/null +++ b/packages/idempotency/API_README.md @@ -0,0 +1,324 @@ + +# Powertools for AWS Lambda (TypeScript) - Idempotency Utility + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement +Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the package in both TypeScript and JavaScript code bases. + +- [Intro](#intro) +- [Usage](#usage) + - [Function wrapper](#function-wrapper) + - [Decorator](#decorator) + - [Middy middleware](#middy-middleware) + - [DynamoDB persistence layer](#dynamodb-persistence-layer) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Intro + +This package provides a utility to implement idempotency in your Lambda functions. +You can either use it to wrap a function, decorate a method, or as Middy middleware to make your AWS Lambda handler +idempotent. + +The current implementation provides a persistence layer for Amazon DynamoDB, which offers a variety of configuration +options. You can also bring your own persistence layer by extending the `BasePersistenceLayer` class. + +## Usage + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/idempotency @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb +``` + +Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow +the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/#iam-permissions) +in the documentation of the utility. + +### Function wrapper + +You can make any function idempotent, and safe to retry, by wrapping it using the `makeIdempotent` higher-order +function. + +The `makeIdempotent` function takes a reference to the function to be made idempotent as first argument, and an object +with options as second argument. + +When you wrap your Lambda handler function, the utility uses the content of the `event` parameter to handle the +idempotency logic. + +```ts +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: 'idempotencyTableName', +}); + +const myHandler = async ( + event: APIGatewayProxyEvent, + _context: Context +): Promise => { + // your code goes here here +}; + +export const handler = makeIdempotent(myHandler, { + persistenceStore, +}); +``` + +You can also use the `makeIdempotent` function to wrap any other arbitrary function, not just Lambda handlers. + +```ts +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: 'idempotencyTableName', +}); + +const processingFunction = async (payload: SQSRecord): Promise => { + // your code goes here here +}; + +const processIdempotently = makeIdempotent(processingFunction, { + persistenceStore, +}); + +export const handler = async ( + event: SQSEvent, + _context: Context +): Promise => { + for (const record of event.Records) { + await processIdempotently(record); + } +}; +``` + +If your function has multiple arguments, you can use the `dataIndexArgument` option to specify which argument should be +used as the idempotency key. + +```ts +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: 'idempotencyTableName', +}); + +const processingFunction = async (payload: SQSRecord, customerId: string): Promise => { + // your code goes here here +}; + +const processIdempotently = makeIdempotent(processingFunction, { + persistenceStore, + // this tells the utility to use the second argument (`customerId`) as the idempotency key + dataIndexArgument: 1, +}); + +export const handler = async ( + event: SQSEvent, + _context: Context +): Promise => { + for (const record of event.Records) { + await processIdempotently(record, 'customer-123'); + } +}; +``` + +Note that you can also specify a JMESPath expression in the Idempotency config object to select a subset of the event +payload as the idempotency key. This is useful when dealing with payloads that contain timestamps or request ids. + +```ts +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: 'idempotencyTableName', +}); + +const myHandler = async ( + event: APIGatewayProxyEvent, + _context: Context +): Promise => { + // your code goes here here +}; + +export const handler = makeIdempotent(myHandler, { + persistenceStore, + config: new IdempotencyConfig({ + eventKeyJmespath: 'requestContext.identity.user', + }), +}); +``` + +Additionally, you can also use one of +the [JMESPath built-in functions](https://docs.aws.amazon.com/powertools/typescript/latest/features/jmespath/#built-in-jmespath-functions) +like `powertools_json()` to decode keys and use parts of the payload as the idempotency key. + +```ts +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: 'idempotencyTableName', +}); + +const myHandler = async ( + event: APIGatewayProxyEvent, + _context: Context +): Promise => { + // your code goes here here +}; + +export const handler = makeIdempotent(myHandler, { + persistenceStore, + config: new IdempotencyConfig({ + eventKeyJmespath: 'powertools_json(body).["user", "productId"]', + }), +}); +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/) for more examples. + +### Decorator + +You can make any function idempotent, and safe to retry, by decorating it using the `@idempotent` decorator. + +```ts +import {idempotent} from '@aws-lambda-powertools/idempotency'; +import {LambdaInterface} from '@aws-lambda-powertools/commons'; +import {DynamoDBPersistenceLayer} from '@aws-lambda-powertools/idempotency/dynamodb'; +import type {Context, APIGatewayProxyEvent} from 'aws-lambda'; + +const persistenceStore = new DynamoDBPersistenceLayer({ + tableName: 'idempotencyTableName', +}); + +class MyHandler extends LambdaInterface { + @idempotent({persistenceStore: dynamoDBPersistenceLayer}) + public async handler( + event: APIGatewayProxyEvent, + context: Context + ): Promise { + // your code goes here here + } +} + +const handlerClass = new MyHandler(); +export const handler = handlerClass.handler.bind(handlerClass); +``` + +Using the same decorator, you can also make any other arbitrary method idempotent. + +```ts +import {idempotent} from '@aws-lambda-powertools/idempotency'; +import {LambdaInterface} from '@aws-lambda-powertools/commons'; +import {DynamoDBPersistenceLayer} from '@aws-lambda-powertools/idempotency/dynamodb'; +import type {Context} from 'aws-lambda'; + +const persistenceStore = new DynamoDBPersistenceLayer({ + tableName: 'idempotencyTableName', +}); + +class MyHandler extends LambdaInterface { + + public async handler( + event: unknown, + context: Context + ): Promise { + for (const record of event.Records) { + await this.processIdempotently(record); + } + } + + @idempotent({persistenceStore: dynamoDBPersistenceLayer}) + private async process(record: unknown): Promise { + // process each code idempotently + } +} + +const handlerClass = new MyHandler(); +export const handler = handlerClass.handler.bind(handlerClass); +``` + +The decorator configuration options are identical with the ones of the `makeIdempotent` function. Check +the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/) for more examples. + +### Middy middleware + +If instead you use Middy, you can use the `makeHandlerIdempotent` middleware. When using the middleware your Lambda +handler becomes idempotent. + +By default, the Idempotency utility will use the full event payload to create an hash and determine if a request is +idempotent, and therefore it should not be retried. +When dealing with a more elaborate payload, where parts of the payload always change you should use +the `IdempotencyConfig` object to instruct the utility to only use a portion of your payload. This is useful when +dealing with payloads that contain timestamps or request ids. + +```ts +import {IdempotencyConfig} from '@aws-lambda-powertools/idempotency'; +import {makeHandlerIdempotent} from '@aws-lambda-powertools/idempotency/middleware'; +import {DynamoDBPersistenceLayer} from '@aws-lambda-powertools/idempotency/dynamodb'; +import middy from '@middy/core'; +import type {Context, APIGatewayProxyEvent} from 'aws-lambda'; + +const persistenceStore = new DynamoDBPersistenceLayer({ + tableName: 'idempotencyTableName', +}); +const config = new IdempotencyConfig({ + hashFunction: 'md5', + useLocalCache: false, + expiresAfterSeconds: 3600, + throwOnNoIdempotencyKey: false, + eventKeyJmesPath: 'headers.idempotency-key', +}); + +export const handler = middy( + async (_event: APIGatewayProxyEvent, _context: Context): Promise => { + // your code goes here here + } +).use( + makeHandlerIdempotent({ + config, + persistenceStore, + }) +); +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/) for more examples. + +### DynamoDB persistence layer + +You can use a DynamoDB Table to store the idempotency information. This enables you to keep track of the hash key, +payload, status for progress, expiration, and much more. + +You can customize most of the configuration options of the table, i.e the names of the attributes. +See +the [API documentation](https://docs.aws.amazon.com/powertools/typescript/latest/api/types/_aws-lambda-powertools_idempotency.types.DynamoDBPersistenceOptions.html) +for more details. + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/idempotency/typedoc.json b/packages/idempotency/typedoc.json index a429c1b85f..0e8b32d3cd 100644 --- a/packages/idempotency/typedoc.json +++ b/packages/idempotency/typedoc.json @@ -9,5 +9,5 @@ "./src/persistence/CachePersistenceLayer.ts", "./src/types/CachePersistence.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/jmespath/API_README.md b/packages/jmespath/API_README.md new file mode 100644 index 0000000000..baa3e62c0b --- /dev/null +++ b/packages/jmespath/API_README.md @@ -0,0 +1,185 @@ +# Powertools for AWS Lambda (TypeScript) - JMESPath Utility + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the package in both TypeScript and JavaScript code bases. + +- [Intro](#intro) +- [Usage](#usage) + - [Basic usage](#basic-usage) + - [Extract data from envelopes](#extract-data-from-envelopes) + - [JMESPath custom functions](#jmespath-custom-functions) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Intro + +The JMESPath utility is a high-level function to parse and extract data from JSON objects using JMESPath expressions. + +## Usage + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/jmespath +``` + +### Basic usage + +At its core, the library provides a utility function to extract data from a JSON object using a JMESPath expression. + +```ts +import { search } from '@aws-lambda-powertools/jmespath'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +type MyEvent = { + foo: { + bar: string; + }; +} + +export const handler = async (event: MyEvent): Promise => { + const result = search(event, 'foo.bar'); + logger.info(result); // "baz" +}; +``` + +### Extract data from envelopes + +In some cases, you may want to extract data from an envelope. The library provides a utility function to help you work with envelopes and extract data from them. + +```ts +import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; + +type MyEvent = { + body: string; // "{\"customerId\":\"dd4649e6-2484-4993-acb8-0f9123103394\"}" + deeplyNested: Array<{ someData: number[] }>; +}; + +type MessageBody = { + customerId: string; +}; + +export const handler = async (event: MyEvent): Promise => { + const payload = extractDataFromEnvelope( + event, + 'powertools_json(body)' + ); + const { customerId } = payload; // now deserialized + + // also works for fetching and flattening deeply nested data + const someData = extractDataFromEnvelope( + event, + 'deeplyNested[*].someData[]' + ); + + return { + customerId, + message: 'success', + context: someData, + statusCode: 200, + }; +}; +``` + +The library provides [a set of built-in envelopes](https://docs.aws.amazon.com/powertools/typescript/latest/features/jmespath/#built-in-envelopes) to help you extract data from common event sources, such as S3, SQS, and SNS, and more. + +```ts +import { + extractDataFromEnvelope, + SQS, +} from '@aws-lambda-powertools/jmespath/envelopes'; +import { Logger } from '@aws-lambda-powertools/logger'; +import type { SQSEvent } from 'aws-lambda'; + +const logger = new Logger(); + +type MessageBody = { + customerId: string; +}; + +export const handler = async (event: SQSEvent): Promise => { + const records = extractDataFromEnvelope>(event, SQS); + for (const record of records) { + // records is now a list containing the deserialized body of each message + const { customerId } = record; + logger.appendKeys({ customerId }); + } +}; +``` + +### JMESPath custom functions + +In addition to all the [built-in JMESPath functions](https://jmespath.org/specification.html#built-in-functions), the library provides custom functions to help you work with complex data structures. For example, you can use the `powertools_json` function to parse a JSON string, or the `powertools_base64` function to decode a base64-encoded string: + +```ts +import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +export const handler = async (event: { payload: string }): Promise => { + const data = extractDataFromEnvelope( + event, + 'powertools_json(powertools_base64(payload))' + ); + + logger.info('Decoded payload', { data }); +}; +``` + +Finally, you can also extend the library with your own custom functions. Below an example of how to create a custom function to decode a Brotli-compressed string. + +```ts +import { fromBase64 } from '@aws-lambda-powertools/commons/utils/base64'; +import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; +import { PowertoolsFunctions } from '@aws-lambda-powertools/jmespath/functions'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { brotliDecompressSync } from 'node:zlib'; + +const logger = new Logger(); + +class CustomFunctions extends PowertoolsFunctions { + @PowertoolsFunctions.signature({ + argumentsSpecs: [['string']], + variadic: false, + }) + public funcDecodeBrotliCompression(value: string): string { + const encoded = fromBase64(value, 'base64'); + const uncompressed = brotliDecompressSync(encoded); + + return uncompressed.toString(); + } +} + +export const handler = async (event: { payload: string }): Promise => { + const message = extractDataFromEnvelope( + event, + 'Records[*].decode_brotli_compression(notification) | [*].powertools_json(@).message', + { customFunctions: new CustomFunctions() } + ); + + logger.info('Decoded message', { message }); +}; +``` + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/jmespath/typedoc.json b/packages/jmespath/typedoc.json index 480beba0f8..f53835eaab 100644 --- a/packages/jmespath/typedoc.json +++ b/packages/jmespath/typedoc.json @@ -7,5 +7,5 @@ "./src/Functions.ts", "./src/PowertoolsFunctions.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/kafka/API_README.md b/packages/kafka/API_README.md new file mode 100644 index 0000000000..851f572505 --- /dev/null +++ b/packages/kafka/API_README.md @@ -0,0 +1,265 @@ +# Powertools for AWS Lambda (TypeScript) - Kafka Utility + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the package in both TypeScript and JavaScript code bases. + +## Intro + +The Kafka Consumer utility transparently handles message deserialization, provides an intuitive developer experience, and integrates seamlessly with the rest of the Powertools for AWS Lambda ecosystem. + +## Usage + +To get started, depending on the schema types you want to use, install the library and the corresponding libraries: + +For JSON schemas: + +```bash +npm install @aws-lambda-powertools/kafka +``` + +For Avro schemas: + +```bash +npm install @aws-lambda-powertools/kafka avro-js +``` + +For Protobuf schemas: + +```bash +npm install @aws-lambda-powertools/kafka protobufjs +``` + +Additionally, if you want to use output parsing with [Standard Schema](https://github.com/standard-schema/standard-schema), you can install [any of the supported libraries](https://standardschema.dev/#what-schema-libraries-implement-the-spec), for example: Zod, Valibot, or ArkType. + +### Deserialization + +The Kafka consumer utility transforms raw Kafka events into an intuitive format for processing. To handle messages effectively, you'll need to configure a schema that matches your data format. + +#### JSON Schema + +```ts +import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; +import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger({ serviceName: 'kafka-consumer' }); + +const schemaConfig = { + value: { + type: SchemaType.JSON, + }, +} satisfies SchemaConfig; + +export const handler = kafkaConsumer(async (event, _context) => { + for (const { value } of event.records) { + logger.info('received value', { value }); + } +}, schemaConfig); +``` + +#### Avro Schema + +```ts +import { readFileSync } from 'node:fs'; +import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; +import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger({ serviceName: 'kafka-consumer' }); + +const schemaConfig = { + value: { + type: SchemaType.AVRO, + schema: readFileSync(new URL('./user.avsc', import.meta.url), 'utf8'), + }, +} satisfies SchemaConfig; + +export const handler = kafkaConsumer(async (event, _context) => { + for (const { value } of event.records) { + logger.info('received value', { value }); + } +}, schemaConfig); +``` + +#### Protobuf Schema + +```ts +import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; +import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { User } from './samples/user.es6.generated.js'; // protobuf generated class + +const logger = new Logger({ serviceName: 'kafka-consumer' }); + +const schemaConfig = { + value: { + type: SchemaType.PROTOBUF, + schema: User, + }, +} satisfies SchemaConfig; + +export const handler = kafkaConsumer(async (event, _context) => { + for (const { value } of event.records) { + logger.info('received value', { value }); + } +}, schemaConfig); +``` + +### Additional Parsing + +You can parse deserialized data using your preferred parsing library. This can help you integrate Kafka data with your domain schemas and application architecture, providing type hints, runtime parsing and validation, and advanced data transformations. + +#### Zod + +```ts +import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; +import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { z } from 'zod/v4'; + +const logger = new Logger({ serviceName: 'kafka-consumer' }); + +const OrderItemSchema = z.object({ + productId: z.string(), + quantity: z.number().int().positive(), + price: z.number().positive(), +}); + +const OrderSchema = z.object({ + id: z.string(), + customerId: z.string(), + items: z.array(OrderItemSchema).min(1, 'Order must have at least one item'), + createdAt: z.iso.datetime(), + totalAmount: z.number().positive(), +}); + +const schemaConfig = { + value: { + type: SchemaType.JSON, + parserSchema: OrderSchema, + }, +} satisfies SchemaConfig; + +export const handler = kafkaConsumer>( + async (event, _context) => { + for (const record of event.records) { + const { + value: { id, items }, + } = record; + logger.setCorrelationId(id); + logger.debug(`order includes ${items.length} items`); + } + }, + schemaConfig +); +``` + +#### Valibot + +```ts +import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; +import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; +import { Logger } from '@aws-lambda-powertools/logger'; +import * as v from 'valibot'; + +const logger = new Logger({ serviceName: 'kafka-consumer' }); + +const OrderItemSchema = v.object({ + productId: v.string(), + quantity: v.pipe(v.number(), v.integer(), v.toMinValue(1)), + price: v.pipe(v.number(), v.integer()), +}); + +const OrderSchema = v.object({ + id: v.string(), + customerId: v.string(), + items: v.pipe( + v.array(OrderItemSchema), + v.minLength(1, 'Order must have at least one item') + ), + createdAt: v.pipe(v.string(), v.isoDateTime()), + totalAmount: v.pipe(v.number(), v.toMinValue(0)), +}); + +const schemaConfig = { + value: { + type: SchemaType.JSON, + parserSchema: OrderSchema, + }, +} satisfies SchemaConfig; + +export const handler = kafkaConsumer>( + async (event, _context) => { + for (const record of event.records) { + const { + value: { id, items }, + } = record; + logger.setCorrelationId(id); + logger.debug(`order includes ${items.length} items`); + } + }, + schemaConfig +); +``` + +#### ArkType + +```ts +import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; +import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { type } from 'arktype'; + +const logger = new Logger({ serviceName: 'kafka-consumer' }); + +const OrderItemSchema = type({ + productId: 'string', + quantity: 'number.integer >= 1', + price: 'number.integer', +}); + +const OrderSchema = type({ + id: 'string', + customerId: 'string', + items: OrderItemSchema.array().moreThanLength(0), + createdAt: 'string.date', + totalAmount: 'number.integer >= 0', +}); + +const schemaConfig = { + value: { + type: SchemaType.JSON, + parserSchema: OrderSchema, + }, +} satisfies SchemaConfig; + +export const handler = kafkaConsumer( + async (event, _context) => { + for (const record of event.records) { + const { + value: { id, items }, + } = record; + logger.setCorrelationId(id); + logger.debug(`order includes ${items.length} items`); + } + }, + schemaConfig +); +``` + +See the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/kafka) for more details on how to use the Kafka utility. + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +* **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +* **Email**: diff --git a/packages/kafka/typedoc.json b/packages/kafka/typedoc.json index dafd6a578c..389b92f2d2 100644 --- a/packages/kafka/typedoc.json +++ b/packages/kafka/typedoc.json @@ -1,5 +1,5 @@ { "extends": ["../../typedoc.base.json"], "entryPoints": ["./src/index.ts", "./src/types/types.ts", "./src/errors.ts"], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/logger/API_README.md b/packages/logger/API_README.md new file mode 100644 index 0000000000..ab0fc6e779 --- /dev/null +++ b/packages/logger/API_README.md @@ -0,0 +1,219 @@ +# Powertools for AWS Lambda (TypeScript) + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the library in both TypeScript and JavaScript code bases. + +- [Intro](#intro) +- [Usage](#usage) + - [Basic usage](#basic-usage) + - [Inject Lambda context](#inject-lambda-context) + - [Logging incoming event](#logging-incoming-event) + - [Append additional keys and data](#append-additional-keys-and-data) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Intro + +The Logger utility provides a structured logging experience with additional features tailored for AWS Lambda functions. + +## Usage + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/logger +``` + +### Basic usage + +Initialize the logger with a service name and log messages: + +```ts +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger({ serviceName: 'serverlessAirline' }); + +export const handler = async (_event, _context): Promise => { + logger.info('Hello World'); + +}; +``` + +You can also log errors and additional data: + +```ts +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +export const handler = async ( + _event: unknown, + _context: unknown +): Promise => { + try { + throw new Error('Unexpected error #1'); + } catch (error) { + // Log information about the error using the default "error" key + logger.error('This is the first error', error as Error); + } + + try { + throw new Error('Unexpected error #2'); + } catch (error) { + // Log information about the error using a custom "myCustomErrorKey" key + logger.error('This is the second error', { + myCustomErrorKey: error as Error, + }); + } +}; +``` + +### Inject Lambda context + +You can enrich your structured logs with key Lambda context information: + +```ts +import { Logger } from '@aws-lambda-powertools/logger'; +import type { Context } from 'aws-lambda'; + +const logger = new Logger(); + +export const handler = async ( + _event: unknown, + context: Context +): Promise => { + logger.addContext(context); + + logger.info('This is an INFO log with some context'); +}; +``` + +The log statement will look like this: + +```json +{ + "cold_start": true, + "function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:shopping-cart-api-lambda-prod-eu-west-1", + "function_memory_size": 128, + "function_request_id": "c6af9ac6-7b61-11e6-9a41-93e812345678", + "function_name": "shopping-cart-api-lambda-prod-eu-west-1", + "level": "INFO", + "message": "This is an INFO log with some context", + "service": "serverlessAirline", + "timestamp": "2021-12-12T21:21:08.921Z", + "xray_trace_id": "abcdef123456abcdef123456abcdef123456" +} +``` + +### Logging incoming event + +You can log the incoming event with (here as decorator, works also as middy middleware): + +```ts +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +class Lambda implements LambdaInterface { + // Set the log event flag to true + @logger.injectLambdaContext({ logEvent: true }) + public async handler(_event: unknown, _context: unknown): Promise { + logger.info('This is an INFO log with some context'); + } +} + +const myFunction = new Lambda(); +export const handler = myFunction.handler.bind(myFunction); // +``` + +### Append additional keys and data + +Append additional keys: + +```ts +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +export const handler = async ( + event: unknown, + _context: unknown +): Promise => { + const myImportantVariable = { + foo: 'bar', + }; + + // Log additional data in single log items + // As second parameter + logger.info('This is a log with an extra variable', { + data: myImportantVariable, + }); + + // You can also pass multiple parameters containing arbitrary objects + logger.info( + 'This is a log with 3 extra objects', + { data: myImportantVariable }, + { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }, + { lambdaEvent: event } + ); + + // Simply pass a string for logging additional data + logger.info('This is a log with additional string value', 'string value'); + + // Directly passing an object containing both the message and the additional info + const logObject = { + message: 'This is a log message', + additionalValue: 42, + }; + + logger.info(logObject); + + return { + foo: 'bar', + }; +}; +``` + +Add **persistent keys** to the logger instance: + +```ts +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger({ + serviceName: 'serverlessAirline', + persistentKeys: { + environment: 'prod', + version: process.env.BUILD_VERSION, + }, +}); + +export const handler = async ( + _event: unknown, + _context: unknown +): Promise => { + logger.info('processing transaction'); + + // ... your business logic +}; +``` + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/logger/typedoc.json b/packages/logger/typedoc.json index 7df20e3456..1fa87ebc91 100644 --- a/packages/logger/typedoc.json +++ b/packages/logger/typedoc.json @@ -5,5 +5,5 @@ "./src/types/index.ts", "./src/middleware/middy.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/metrics/API_README.md b/packages/metrics/API_README.md new file mode 100644 index 0000000000..94593f8359 --- /dev/null +++ b/packages/metrics/API_README.md @@ -0,0 +1,144 @@ +# Powertools for AWS Lambda (TypeScript) + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the library in both TypeScript and JavaScript code bases. + +- [Usage](#usage) + - [Flushing metrics](#flushing-metrics) + - [Capturing cold start as a metric](#capturing-cold-start-as-a-metric) + - [Class method decorator](#class-method-decorator) + - [Middy.js middleware](#middyjs-middleware) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Usage + +The library provides a utility function to emit metrics to CloudWatch using [Embedded Metric Format (EMF)](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html). + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/metrics +``` + +After initializing the Metrics class, you can add metrics using the [https://docs.aws.amazon.com/powertools/typescript/latest/core/metrics/#creating-metrics](`addMetric()`) method. The metrics are stored in a buffer and are flushed when calling [https://docs.aws.amazon.com/powertools/typescript/latest/core/metrics/#flushing-metrics](`publishStoredMetrics()`). + +Each metric can also have dimensions and metadata added to it. + +```ts +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', + defaultDimensions: { environment: process.env.ENVIRONMENT }, +}); + +export const handler = async (event: { requestId: string }) => { + metrics.addMetadata('request_id', event.requestId); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); + metrics.publishStoredMetrics(); +}; +``` + +### Flushing metrics + +As you finish adding all your metrics, you need to serialize and "flush them" by calling `publishStoredMetrics()`, which will emit the metrics to stdout in the Embedded Metric Format (EMF). The metrics are then picked up by the Lambda runtime and sent to CloudWatch. + +The `publishStoredMetrics()` method is synchronous and will block the event loop until the metrics are flushed. If you want Metrics to flush automatically at the end of your Lambda function, you can use the `@logMetrics()` decorator or the `logMetrics()` middleware. + +### Capturing cold start as a metric + +With Metrics, you can capture cold start as a metric by calling the `captureColdStartMetric()` method. This method will add a metric with the name `ColdStart` and the value `1` to the metrics buffer. + +This metric is flushed automatically as soon as the method is called, to ensure that the cold start is captured regardless of whether the metrics are flushed manually or automatically. + +```ts +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', +}); + +export const handler = async (event: { requestId: string }) => { + metrics.captureColdStartMetric(); +}; +``` + +Note that we don't emit a `ColdStart` metric with value `0` when the function is warm, as this would result in a high volume of metrics being emitted to CloudWatch, so you'll need to rely on the absence of the `ColdStart` metric to determine if the function is warm. + +### Class method decorator + +If you are using TypeScript and are comfortable with writing classes, you can use the `@logMetrics()` decorator to automatically flush metrics at the end of your Lambda function as well as configure additional options such as throwing an error if no metrics are added, capturing cold start as a metric, and more. + +```ts +import type { Context } from 'aws-lambda'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; + +const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', +}); + +class Lambda implements LambdaInterface { + ⁣@metrics.logMetrics({ captureColdStartMetric: true, throwOnEmptyMetrics: true }) + public async handler(event: { requestId: string }, _: Context) { + metrics.addMetadata('request_id', event.requestId); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); + } +} + +const handlerClass = new Lambda(); +export const handler = handlerClass.handler.bind(handlerClass); +``` + +Decorators are a Stage 3 proposal for JavaScript and are not yet part of the ECMAScript standard. The current implmementation in this library is based on the legacy TypeScript decorator syntax enabled by the [`experimentalDecorators` flag](https://www.typescriptlang.org/tsconfig/#experimentalDecorators) set to `true` in the `tsconfig.json` file. + +### Middy.js middleware + +If instead you are using [Middy.js](http://middy.js.org) and prefer to use middleware, you can use the `@logMetrics()` middleware to do the same as the class method decorator. + +The `@logMetrics()` middleware can be used with Middy.js to automatically flush metrics at the end of your Lambda function as well as configure additional options such as throwing an error if no metrics are added, capturing cold start as a metric, and set default dimensions. + +```ts +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; +import middy from '@middy/core'; + +const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', +}); + +export const handler = middy(async (event) => { + metrics.addMetadata('request_id', event.requestId); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); +}).use(logMetrics(metrics, { + captureColdStartMetric: true, + throwOnEmptyMetrics: true, +})); +``` + +The `logMetrics()` middleware is compatible with `@middy/core@3.x` and above. + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/metrics/typedoc.json b/packages/metrics/typedoc.json index 7df20e3456..1fa87ebc91 100644 --- a/packages/metrics/typedoc.json +++ b/packages/metrics/typedoc.json @@ -5,5 +5,5 @@ "./src/types/index.ts", "./src/middleware/middy.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/parameters/API_README.md b/packages/parameters/API_README.md new file mode 100644 index 0000000000..6ada8a66df --- /dev/null +++ b/packages/parameters/API_README.md @@ -0,0 +1,209 @@ +# Powertools for AWS Lambda (TypeScript) + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the package in both TypeScript and JavaScript code bases. + +- [Intro](#intro) +- [Usage](#usage) + - [Fetching parameters from AWS SSM Parameter Store](#fetching-parameters-from-aws-ssm-parameter-store) + - [Getting secrets from Amazon Secrets Manager](#getting-secrets-from-amazon-secrets-manager) + - [Retrieving values from Amazon DynamoDB](#retrieving-values-from-amazon-dynamodb) + - [Fetching configs from AWS AppConfig](#fetching-configs-from-aws-appconfig) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Intro + +The Parameters utility provides high-level functions to retrieve one or multiple parameter values from + +- [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) +- [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html), +- [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html), +- [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html), +- or your own parameter store. + +## Usage + +### Fetching parameters from AWS SSM Parameter Store + +To get started, install the library and the corresponding AWS SDK for JavaScript v3: + +```sh +npm install @aws-lambda-powertools/parameters @aws-sdk/client-ssm +``` + +Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. + +You can retrieve a single parameter using the `getParameter` high-level function. + +```ts +import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; + +export const handler = async (): Promise => { + // Retrieve a single parameter + const parameter = await getParameter('/my/parameter'); + console.log(parameter); +}; +``` + +For multiple parameters, you can use `getParameters` to recursively fetch all parameters under a path: + +```ts +import { getParameters } from '@aws-lambda-powertools/parameters/ssm'; + +export const handler = async (): Promise => { + /** + * Retrieve multiple parameters from a path prefix recursively. + * This returns an object with the parameter name as key + */ + const parameters = await getParameters('/my/path/prefix'); + for (const [key, value] of Object.entries(parameters || {})) { + console.log(`${key}: ${value}`); + } +}; +``` + +To fetch distinct parameters using their full name, you can use the `getParametersByName` function: + +```ts +import { Transform } from '@aws-lambda-powertools/parameters'; +import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; +import type { SSMGetParametersByNameOptions } from '@aws-lambda-powertools/parameters/ssm/types'; + +const props: Record = { + '/develop/service/commons/telemetry/config': { + maxAge: 300, + transform: Transform.JSON, + }, + '/no_cache_param': { maxAge: 0 }, + '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values +}; + +export const handler = async (): Promise => { + // This returns an object with the parameter name as key + const parameters = await getParametersByName(props, { maxAge: 60 }); + for (const [key, value] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } +}; +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-parameters) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. + +### Getting secrets from Amazon Secrets Manager + +To get started, install the library and the corresponding AWS SDK for JavaScript v3: + +```sh +npm install @aws-lambda-powertools/parameters @aws-sdk/client-secrets-manager +``` + +Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. + +You can fetch secrets stored in Secrets Manager using the `getSecret` function: + +```ts +import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; + +export const handler = async (): Promise => { + // Retrieve a single secret + const secret = await getSecret('my-secret'); + console.log(secret); +}; +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-secrets) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. + +### Retrieving values from Amazon DynamoDB + +To get started, install the library and the corresponding AWS SDK for JavaScript v3: + +```sh +npm install @aws-lambda-powertools/parameters @aws-sdk/client-dynamodb @aws-sdk/util-dynamodb +``` + +Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. + +You can retrieve a single parameter from DynamoDB using the `DynamoDBProvider.get()` method: + +```ts +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + +const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); + +export const handler = async (): Promise => { + // Retrieve a value from DynamoDB + const value = await dynamoDBProvider.get('my-parameter'); + console.log(value); +}; +``` + +For retrieving multiple parameters, you can use the `DynamoDBProvider.getMultiple()` method instead: + +```ts +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + +const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); + +export const handler = async (): Promise => { + /** + * Retrieve multiple values by performing a Query on the DynamoDB table. + * This returns a dict with the sort key attribute as dict key. + */ + const values = await dynamoDBProvider.getMultiple('my-hash-key'); + for (const [key, value] of Object.entries(values || {})) { + // key: param-a + // value: my-value-a + console.log(`${key}: ${value}`); + } +}; +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-secrets) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. + +### Fetching configs from AWS AppConfig + +To get started, install the library and the corresponding AWS SDK for JavaScript v3: + +```sh +npm install @aws-lambda-powertools/parameters @aws-sdk/client-appconfigdata +``` + +Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. + +You can fetch application configurations in AWS AppConfig using the `getAppConfig` function: + +```ts +import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig'; + +export const handler = async (): Promise => { + // Retrieve a configuration, latest version + const config = await getAppConfig('my-configuration', { + environment: 'my-env', + application: 'my-app', + }); + console.log(config); +}; +``` + +Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-app-configurations) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/parameters/typedoc.json b/packages/parameters/typedoc.json index 6f77a07a12..3c36f17ba3 100644 --- a/packages/parameters/typedoc.json +++ b/packages/parameters/typedoc.json @@ -13,7 +13,7 @@ "./src/errors.ts", "./src/constants.ts" ], - "readme": "README.md", + "readme": "./API_README.md", "externalSymbolLinkMappings": { "@aws-sdk/client-secrets-manager": { "GetSecretValueCommandInput": "https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-client-secrets-manager/Interface/GetSecretValueCommandInput/" diff --git a/packages/parser/API_README.md b/packages/parser/API_README.md new file mode 100644 index 0000000000..29ad75b720 --- /dev/null +++ b/packages/parser/API_README.md @@ -0,0 +1,314 @@ +# Powertools for AWS Lambda (TypeScript) - Parser Utility + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the package in both TypeScript and JavaScript code bases. + +- [Intro](#intro) +- [Key features](#key-features) +- [Usage](#usage) + - [Middy.js Middleware](#middyjs-middleware) + - [Decorator](#decorator) + - [Manual parsing](#manual-parsing) + - [Safe parsing](#safe-parsing) + - [Built-in schemas and envelopes](#built-in-schemas-and-envelopes) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Intro + +This utility provides data validation and parsing for [Standard Schema](https://github.com/standard-schema/standard-schema), together with a collection of built-in [Zod](https://zod.dev) schemas and envelopes to parse and unwrap popular AWS event source payloads. + +## Key features + +- Accept a [Standard Schema](https://github.com/standard-schema/standard-schema) and parse incoming payloads +- Built-in Zod schemas and envelopes to unwrap and validate popular AWS event sources payloads +- Extend and customize built-in Zod schemas to fit your needs +- Safe parsing option to avoid throwing errors and allow custom error handling +- Available as Middy.js middleware and TypeScript class method decorator + +## Usage + +To get started, install the library by running: + +```sh +npm install @aws-lambda-powertools/parser zod +``` + +You can parse inbound events using the `parser` decorator, Middy.js middleware, or [manually](#manual-parsing) using built-in envelopes and schemas. + +When using the decorator or middleware, you can specify a schema to parse the event: this can be a [built-in Zod schema](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser/#built-in-schemas) or a custom schema you defined. Custom schemas can be defined using Zod or any other [Standard Schema compatible library](https://standardschema.dev/#what-schema-libraries-implement-the-spec). + +### Middy.js Middleware + +Using Zod schemas: + +```typescript +import { Logger } from '@aws-lambda-powertools/logger'; +import { parser } from '@aws-lambda-powertools/parser/middleware'; +import middy from '@middy/core'; +import { z } from 'zod'; + +const logger = new Logger(); + +const orderSchema = z.object({ + id: z.number().positive(), + description: z.string(), + items: z.array( + z.object({ + id: z.number().positive(), + quantity: z.number().positive(), + description: z.string(), + }) + ), + optionalField: z.string().optional(), +}); + +export const handler = middy() + .use(parser({ schema: orderSchema })) + .handler(async (event): Promise => { + for (const item of event.items) { + // item is parsed as OrderItem + logger.info('Processing item', { item }); + } + }); +``` + +Using Valibot schemas: + +```typescript +import { Logger } from '@aws-lambda-powertools/logger'; +import { parser } from '@aws-lambda-powertools/parser/middleware'; +import middy from '@middy/core'; +import { + array, + number, + object, + optional, + pipe, + string, + toMinValue, +} from 'valibot'; + +const logger = new Logger(); + +const orderSchema = object({ + id: pipe(number(), toMinValue(0)), + description: string(), + items: array( + object({ + id: pipe(number(), toMinValue(0)), + quantity: pipe(number(), toMinValue(1)), + description: string(), + }) + ), + optionalField: optional(string()), +}); + +export const handler = middy() + .use(parser({ schema: orderSchema })) + .handler(async (event): Promise => { + for (const item of event.items) { + // item is parsed as OrderItem + logger.info('Processing item', { item }); + } + }); +``` + +### Decorator + +```typescript +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { parser } from '@aws-lambda-powertools/parser'; +import type { Context } from 'aws-lambda'; +import { z } from 'zod'; + +const logger = new Logger(); + +const orderSchema = z.object({ + id: z.number().positive(), + description: z.string(), + items: z.array( + z.object({ + id: z.number().positive(), + quantity: z.number(), + description: z.string(), + }) + ), + optionalField: z.string().optional(), +}); + +type Order = z.infer; + +class Lambda implements LambdaInterface { + @parser({ schema: orderSchema }) + public async handler(event: Order, _context: Context): Promise { + // event is now typed as Order + for (const item of event.items) { + logger.info('Processing item', { item }); + } + } +} + +const myFunction = new Lambda(); +export const handler = myFunction.handler.bind(myFunction); +``` + +### Manual parsing + +If you don't want to add an additional middleware dependency, or you prefer the manual approach, you can parse the event directly by calling the `parse` method on schemas and envelopes: + +```typescript +import type { Context } from 'aws-lambda'; +import { z } from 'zod'; +import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes'; +import { EventBridgeSchema } from '@aws-lambda-powertools/parser/schemas'; +import type { EventBridgeEvent } from '@aws-lambda-powertools/parser/types'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +const orderSchema = z.object({ + id: z.number().positive(), + description: z.string(), + items: z.array( + z.object({ + id: z.number().positive(), + quantity: z.number(), + description: z.string(), + }) + ), + optionalField: z.string().optional(), +}); +type Order = z.infer; + +export const handler = async ( + event: EventBridgeEvent, + _context: Context +): Promise => { + const parsedEvent = EventBridgeSchema.parse(event); + logger.info('Parsed event', parsedEvent); + + const orders: Order = EventBridgeEnvelope.parse(event, orderSchema); + logger.info('Parsed orders', orders); +}; +``` + +### Safe parsing + +When parsing data, you can use the `safeParse` method to avoid throwing errors and handle them manually: + +```typescript +import type { Context } from 'aws-lambda'; +import { parser } from '@aws-lambda-powertools/parser/middleware'; +import { z } from 'zod'; +import middy from '@middy/core'; +import type { + ParsedResult, + EventBridgeEvent, +} from '@aws-lambda-powertools/parser/types'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +const orderSchema = z.object({ + id: z.number().positive(), + description: z.string(), + items: z.array( + z.object({ + id: z.number().positive(), + quantity: z.number(), + description: z.string(), + }) + ), + optionalField: z.string().optional(), +}); + +type Order = z.infer; + +const lambdaHandler = async ( + event: ParsedResult, + _context: Context +): Promise => { + if (event.success) { + // (2)! + for (const item of event.data.items) { + logger.info('Processing item', { item }); + } + } else { + logger.error('Error parsing event', { event: event.error }); + logger.error('Original event', { event: event.originalEvent }); + } +}; + +export const handler = middy(lambdaHandler).use( + parser({ schema: orderSchema, safeParse: true }) +); +``` + +See the [safe parsing](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser#safe-parsing) section in the documentation for more details. + +### Built-in schemas and envelopes + +The utility provides a set of built-in schemas and envelopes to parse popular AWS event sources payloads, for example: + +```typescript +import type { Context } from 'aws-lambda'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { parser } from '@aws-lambda-powertools/parser'; +import { z } from 'zod'; +import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes'; +import { Logger } from '@aws-lambda-powertools/logger'; + +const logger = new Logger(); + +const orderSchema = z.object({ + id: z.number().positive(), + description: z.string(), + items: z.array( + z.object({ + id: z.number().positive(), + quantity: z.number(), + description: z.string(), + }) + ), + optionalField: z.string().optional(), +}); + +type Order = z.infer; + +class Lambda implements LambdaInterface { + @parser({ schema: orderSchema, envelope: EventBridgeEnvelope }) + public async handler(event: Order, _context: Context): Promise { + // event is now typed as Order + for (const item of event.items) { + logger.info('Processing item', item); // (2)! + } + } +} + +const myFunction = new Lambda(); +export const handler = myFunction.handler.bind(myFunction); +``` + +Check the utility documentation for a complete list of built-in [schemas](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser/#built-in-schemas) and [envelopes](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser/#built-in-envelopes). + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/parser/typedoc.json b/packages/parser/typedoc.json index cc06598557..2a4df3b89c 100644 --- a/packages/parser/typedoc.json +++ b/packages/parser/typedoc.json @@ -9,5 +9,5 @@ "./src/helpers/index.ts", "./src/helpers/dynamodb.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/tracer/API_README.md b/packages/tracer/API_README.md new file mode 100644 index 0000000000..df3ba7052e --- /dev/null +++ b/packages/tracer/API_README.md @@ -0,0 +1,130 @@ +# Powertools for AWS Lambda (TypeScript) + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). + +You can use the library in both TypeScript and JavaScript code bases. + +- [Intro](#intro) +- [Usage](#usage) + - [Basic usage](#basic-usage) + - [Capture AWS SDK clients](#capture-aws-sdk-clients) + - [Add metadata and annotations](#add-metadata-and-annotations) +- [Contribute](#contribute) +- [Roadmap](#roadmap) +- [Connect](#connect) + - [Becoming a reference customer](#becoming-a-reference-customer) + - [Sharing your work](#sharing-your-work) + - [Using Lambda Layer](#using-lambda-layer) +- [License](#license) + +## Intro + +The Tracer utility is an opinionated thin wrapper for [AWS X-Ray SDK for Node.js](https://github.com/aws/aws-xray-sdk-node), to automatically capture cold starts, trace HTTP(S) clients including `fetch` and generate segments and add metadata or annotations to traces. + +## Usage + +To get started, install the library by running: + +```sh +npm i @aws-lambda-powertools/tracer +``` + +### Basic usage + +Add `Tracer` to your Lambda handler as decorator: + +```ts +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { Tracer } from '@aws-lambda-powertools/tracer'; + +const tracer = new Tracer({ serviceName: 'serverlessAirline' }); + +class Lambda implements LambdaInterface { + // Decorate your handler class method + @tracer.captureLambdaHandler() + public async handler(_event: unknown, _context: unknown): Promise { + tracer.getSegment(); + } +} + +const handlerClass = new Lambda(); +export const handler = handlerClass.handler.bind(handlerClass); +``` + +or using middy: + +```ts +import { Tracer } from '@aws-lambda-powertools/tracer'; +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; +import middy from '@middy/core'; + +const tracer = new Tracer({ serviceName: 'serverlessAirline' }); + +const lambdaHandler = async ( + _event: unknown, + _context: unknown +): Promise => { + tracer.putAnnotation('successfulBooking', true); +}; + +// Wrap the handler with middy +export const handler = middy(lambdaHandler) + // Use the middleware by passing the Tracer instance as a parameter + .use(captureLambdaHandler(tracer)); +``` + +### Capture AWS SDK clients + +To capture AWS SDK clients, you can use the `captureAWSv3Client` method: + +```ts +import { Tracer } from '@aws-lambda-powertools/tracer'; +import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; + +const tracer = new Tracer({ serviceName: 'serverlessAirline' }); +// Instrument the AWS SDK client +const client = tracer.captureAWSv3Client(new SecretsManagerClient({})); + +export default client; +``` + +### Add metadata and annotations + +You can add metadata and annotations to trace: + +```ts + +import { Tracer } from '@aws-lambda-powertools/tracer'; + +const tracer = new Tracer({ serviceName: 'serverlessAirline' }); + +export const handler = async ( + _event: unknown, + _context: unknown +): Promise => { + const handlerSegment = tracer.getSegment()?.addNewSubsegment('### handler'); + handlerSegment && tracer.setSegment(handlerSegment); + + tracer.putMetadata('paymentResponse', { + foo: 'bar', + }); + tracer.putAnnotation('successfulBooking', true); + + handlerSegment?.close(); + handlerSegment && tracer.setSegment(handlerSegment?.parent); +}; +``` + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/tracer/typedoc.json b/packages/tracer/typedoc.json index c1513a9208..9a7cd9dc47 100644 --- a/packages/tracer/typedoc.json +++ b/packages/tracer/typedoc.json @@ -6,5 +6,5 @@ "./src/middleware/middy.ts", "./src/provider/ProviderService.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } diff --git a/packages/validation/API_README.md b/packages/validation/API_README.md new file mode 100644 index 0000000000..91671ad15b --- /dev/null +++ b/packages/validation/API_README.md @@ -0,0 +1,242 @@ +# Powertools for AWS Lambda (TypeScript) - Validation Utility + +This utility provides JSON Schema validation for events and responses, including JMESPath support to unwrap events before validation. + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. + +To get started, install the package by running: + +```sh +npm i @aws-lambda-powertools/validation +``` + +## Features + +You can validate inbound and outbound payloads using the `@validator` class method decorator or `validator` Middy.js middleware. + +You can also use the standalone `validate` function, if you want more control over the validation process such as handling a validation error. + +### Validator decorator + +The `@validator` decorator is a TypeScript class method decorator that you can use to validate both the incoming event and the response payload. + +If the validation fails, we will throw a `SchemaValidationError`. + +```typescript +import { validator } from '@aws-lambda-powertools/validation/decorator'; +import type { Context } from 'aws-lambda'; + +const inboundSchema = { + type: 'object', + properties: { + value: { type: 'number' }, + }, + required: ['value'], + additionalProperties: false, +}; + +const outboundSchema = { + type: 'object', + properties: { + result: { type: 'number' }, + }, + required: ['result'], + additionalProperties: false, +}; + +class Lambda { + @validator({ + inboundSchema, + outboundSchema, + }) + async handler(event: { value: number }, _context: Context) { + // Your handler logic here + return { result: event.value * 2 }; + } +} + +const lambda = new Lambda(); +export const handler = lambda.handler.bind(lambda); +``` + +It's not mandatory to validate both the inbound and outbound payloads. You can either use one, the other, or both. + +### Validator middleware + +If you are using Middy.js, you can instead use the `validator` middleware to validate the incoming event and response payload. + +```typescript +import { validator } from '@aws-lambda-powertools/validation/middleware'; +import middy from '@middy/core'; + +const inboundSchema = { + type: 'object', + properties: { + foo: { type: 'string' }, + }, + required: ['foo'], + additionalProperties: false, +}; + +const outboundSchema = { + type: 'object', + properties: { + bar: { type: 'number' }, + }, + required: ['bar'], + additionalProperties: false, +}; + +export const handler = middy() + .use(validation({ inboundSchema, outboundSchema })) + .handler(async (event) => { + // Your handler logic here + return { bar: 42 }; + }); +``` + +Like the `@validator` decorator, you can choose to validate only the inbound or outbound payload. + +### Standalone validate function + +The `validate` function gives you more control over the validation process, and is typically used within the Lambda handler, or any other function that needs to validate data. + +When using the standalone function, you can gracefully handle schema validation errors by catching `SchemaValidationError` errors. + +```typescript +import { validate } from '@aws-lambda-powertools/validation'; +import { SchemaValidationError } from '@aws-lambda-powertools/validation/errors'; + +const schema = { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number' }, + }, + required: ['name', 'age'], + additionalProperties: false, +} as const; + +const payload = { name: 'John', age: 30 }; + +export const handler = async (event: unknown) => { + try { + const validatedData = validate({ + payload, + schema, + }); + + // Your handler logic here + } catch (error) { + if (error instanceof SchemaValidationError) { + // Handle the validation error + return { + statusCode: 400, + body: JSON.stringify({ message: error.message }), + }; + } + // Handle other errors + throw error; + } +} +``` + +### JMESPath support + +In some cases you might want to validate only a portion of the event payload - this is what the `envelope` option is for. + +You can use JMESPath expressions to specify the path to the property you want to validate. The validator will unwrap the event before validating it. + +```typescript +import { validate } from '@aws-lambda-powertools/validation'; + +const schema = { + type: 'object', + properties: { + user: { type: 'string' }, + }, + required: ['user'], + additionalProperties: false, +} as const; + +const payload = { + data: { + user: 'Alice', + }, +}; + +const validatedData = validate({ + payload, + schema, + envelope: 'data', +}); +``` + +### Extending the validator + +Since the validator is built on top of [Ajv](https://ajv.js.org/), you can extend it with custom formats and external schemas, as well as bringing your own `ajv` instance. + +The example below shows how to pass additional options to the `validate` function, but you can also pass them to the `@validator` decorator and `validator` middleware. + +```typescript +import { validate } from '@aws-lambda-powertools/validation'; + +const formats = { + ageRange: (value: number) => return value >= 0 && value <= 120, +}; + +const definitionSchema = { + $id: 'https://example.com/schemas/definitions.json', + definitions: { + user: { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number', format: 'ageRange' }, + }, + required: ['name', 'age'], + additionalProperties: false, + } + } +} as const; + +const schema = { + $id: 'https://example.com/schemas/user.json', + type: 'object', + properties: { + user: { $ref: 'definitions.json#/definitions/user' }, + }, + required: ['user'], + additionalProperties: false, +} as const; + +const payload = { + user: { + name: 'Alice', + age: 25, + }, +}; + +const validatedData = validate({ + payload, + schema, + externalRefs: [definitionSchema], + formats, +}); +``` + +For more information on how to use the `validate` function, please refer to the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/validation). + +## Contribute + +If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). + +## Roadmap + +The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. +Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. + +## Connect + +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: diff --git a/packages/validation/typedoc.json b/packages/validation/typedoc.json index 0ce66a9f21..a2f821fcaf 100644 --- a/packages/validation/typedoc.json +++ b/packages/validation/typedoc.json @@ -7,5 +7,5 @@ "./src/decorator.ts", "./src/errors.ts" ], - "readme": "README.md" + "readme": "./API_README.md" } From 272c9879151fe40c60b6dc36c79c4d02627af152 Mon Sep 17 00:00:00 2001 From: Zelys Date: Wed, 8 Apr 2026 12:09:24 -0500 Subject: [PATCH 2/2] refactor: generate API_README.md at build time via mkdocs hook Replace committed API_README.md files with a build-time generator. docs/generate_api_readme.py strips the customer reference section from each package README.md and the root README.md, including the TOC entry, then writes API_README.md files that TypeDoc references. Registered as an mkdocs on_pre_build hook so mkdocs build handles generation automatically. The docs:local:api npm script runs it before calling typedoc directly. Generated files are gitignored. --- .gitignore | 5 +- docs/generate_api_readme.py | 44 ++++ mkdocs.yml | 3 + package.json | 2 +- packages/batch/API_README.md | 160 ------------- packages/commons/API_README.md | 99 -------- packages/event-handler/API_README.md | 312 -------------------------- packages/idempotency/API_README.md | 324 --------------------------- packages/jmespath/API_README.md | 185 --------------- packages/kafka/API_README.md | 265 ---------------------- packages/logger/API_README.md | 219 ------------------ packages/metrics/API_README.md | 144 ------------ packages/parameters/API_README.md | 209 ----------------- packages/parser/API_README.md | 314 -------------------------- packages/tracer/API_README.md | 130 ----------- packages/validation/API_README.md | 242 -------------------- typedoc.json | 2 +- 17 files changed, 53 insertions(+), 2606 deletions(-) create mode 100644 docs/generate_api_readme.py delete mode 100644 packages/batch/API_README.md delete mode 100644 packages/commons/API_README.md delete mode 100644 packages/event-handler/API_README.md delete mode 100644 packages/idempotency/API_README.md delete mode 100644 packages/jmespath/API_README.md delete mode 100644 packages/kafka/API_README.md delete mode 100644 packages/logger/API_README.md delete mode 100644 packages/metrics/API_README.md delete mode 100644 packages/parameters/API_README.md delete mode 100644 packages/parser/API_README.md delete mode 100644 packages/tracer/API_README.md delete mode 100644 packages/validation/API_README.md diff --git a/.gitignore b/.gitignore index c8312c40ff..fa697a4139 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,7 @@ tsconfig.tsbuildinfo .amazonq .kiro .github/instructions -aidlc-docs \ No newline at end of file +aidlc-docs +# Generated by docs/generate_api_readme.py - do not commit +API_README.md +packages/*/API_README.md diff --git a/docs/generate_api_readme.py b/docs/generate_api_readme.py new file mode 100644 index 0000000000..597b20fb21 --- /dev/null +++ b/docs/generate_api_readme.py @@ -0,0 +1,44 @@ +""" +Generate API_README.md files for TypeDoc by stripping the customer reference section +from each package's README.md and the root README.md. + +Registered as an mkdocs on_pre_build hook so it runs automatically during `mkdocs build`. +Can also be run standalone: python3 docs/generate_api_readme.py +""" + +import re +from pathlib import Path + + +# Removes the full section body (heading through the last sub-section before ## License) +SECTION_PATTERN = re.compile( + r"\n## How to support Powertools for AWS Lambda \(TypeScript\)\?.*?(?=\n## )", + re.DOTALL, +) + +# Removes the TOC entry and its indented children +TOC_ENTRY_PATTERN = re.compile( + r"\n- \[How to support Powertools for AWS Lambda \(TypeScript\)\?[^\n]*" + r"(?:\n - \[[^\n]*)*", +) + + +def generate(root: Path = Path(".")) -> None: + targets = [root] + sorted((root / "packages").iterdir()) + for pkg in targets: + readme = pkg / "README.md" + if not readme.exists(): + continue + content = readme.read_text(encoding="utf-8") + clean = SECTION_PATTERN.sub("", content) + clean = TOC_ENTRY_PATTERN.sub("", clean) + (pkg / "API_README.md").write_text(clean, encoding="utf-8") + + +# mkdocs hook entry point +def on_pre_build(config) -> None: # noqa: ANN001 + generate() + + +if __name__ == "__main__": + generate() diff --git a/mkdocs.yml b/mkdocs.yml index 79ccff71b1..bfa88bd045 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,3 +1,6 @@ +hooks: + - docs/generate_api_readme.py + site_name: Powertools for AWS Lambda (TypeScript) site_description: Powertools for AWS Lambda (TypeScript) site_author: Amazon Web Services diff --git a/package.json b/package.json index 862e30481d..dc5df8dd87 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "docs:docker:run": "docker run --rm -it -p 8000:8000 -v ${PWD}:/docs powertools-typescript/docs", "docs:local:setup": "python3 -m venv .venv && .venv/bin/pip install -r docs/requirements.txt", "docs:local:run": ".venv/bin/mkdocs serve", - "docs:local:api": "typedoc .", + "docs:local:api": "python3 docs/generate_api_readme.py && typedoc .", "postpublish": "git restore .", "lint:markdown": "markdownlint-cli2 '**/*.md' '#node_modules' '#**/*/node_modules' '#docs/changelog.md' '#LICENSE.md' '#.github' '#CHANGELOG.md' '#**/*/CHANGELOG.md' '#examples/app/README.md' '#.venv' '#site'" }, diff --git a/packages/batch/API_README.md b/packages/batch/API_README.md deleted file mode 100644 index a01f8077a7..0000000000 --- a/packages/batch/API_README.md +++ /dev/null @@ -1,160 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - Batch Processing Utility - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the library in both TypeScript and JavaScript code bases. - -## Intro - -The Batch Processing utility handles partial failures when processing batches from Amazon SQS, Amazon Kinesis Data Streams, and Amazon DynamoDB Streams. - -## Usage - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/batch -``` - -### Batch Processor - -When using SQS, Kinesis Data Streams, or DynamoDB Streams as a Lambda event source, your Lambda functions are triggered with a batch of messages. - -If your function fails to process any message from the batch, the entire batch returns to your queue or stream. This same batch is then retried until either condition happens first: **a)** your Lambda function returns a successful response, **b)** record reaches maximum retry attempts, or **c)** when records expire. - -With this utility, batch records are processed individually – only messages that failed to be processed return to the queue or stream for a further retry. - -### SQS Processor - -When using SQS as a Lambda event source, you can specify the `EventType.SQS` to process the records. The response will be a `SQSBatchResponse` which contains a list of items that failed to be processed. - -```ts -import { - BatchProcessorSync, - EventType, - processPartialResponseSync, -} from '@aws-lambda-powertools/batch'; -import { Logger } from '@aws-lambda-powertools/logger'; -import type { SQSHandler, SQSRecord } from 'aws-lambda'; - -const processor = new BatchProcessorSync(EventType.SQS); -const logger = new Logger(); - -const recordHandler = (record: SQSRecord): void => { - const payload = record.body; - if (payload) { - const item = JSON.parse(payload); - logger.info('Processed item', { item }); - } -}; - -export const handler: SQSHandler = async (event, context) => - processPartialResponseSync(event, recordHandler, processor, { - context, - }); -``` - -### Kinesis Processor - -When using Kinesis Data Streams as a Lambda event source, you can specify the `EventType.KinesisDataStreams` to process the records. The response will be a `KinesisStreamBatchResponse` which contains a list of items that failed to be processed. - -```ts -import { - BatchProcessorSync, - EventType, - processPartialResponseSync, -} from '@aws-lambda-powertools/batch'; -import { Logger } from '@aws-lambda-powertools/logger'; -import type { KinesisStreamHandler, KinesisStreamRecord } from 'aws-lambda'; - -const processor = new BatchProcessorSync(EventType.KinesisDataStreams); -const logger = new Logger(); - -const recordHandler = (record: KinesisStreamRecord): void => { - logger.info('Processing record', { record: record.kinesis.data }); - const payload = JSON.parse(record.kinesis.data); - logger.info('Processed item', { item: payload }); -}; - -export const handler: KinesisStreamHandler = async (event, context) => - processPartialResponseSync(event, recordHandler, processor, { - context, - }); -``` - -### DynamoDB Streams Processor - -When using DynamoDB Streams as a Lambda event source, you can use the `BatchProcessorSync` with the `EventType.DynamoDBStreams` to process the records. The response will be a `DynamoDBBatchResponse` which contains a list of items that failed to be processed. - -```ts -import { - BatchProcessor, - EventType, - processPartialResponseSync, -} from '@aws-lambda-powertools/batch'; -import { Logger } from '@aws-lambda-powertools/logger'; -import type { DynamoDBRecord, DynamoDBStreamHandler } from 'aws-lambda'; - -const processor = new BatchProcessor(EventType.DynamoDBStreams); // (1)! -const logger = new Logger(); - -const recordHandler = (record: DynamoDBRecord): void => { - if (record.dynamodb && record.dynamodb.NewImage) { - logger.info('Processing record', { record: record.dynamodb.NewImage }); - const message = record.dynamodb.NewImage.Message.S; - if (message) { - const payload = JSON.parse(message); - logger.info('Processed item', { item: payload }); - } - } -}; - -export const handler: DynamoDBStreamHandler = async (event, context) => - processPartialResponseSync(event, recordHandler, processor, { - context, - }); -``` - -### Async processing - -If your use case allows you to process multiple records at the same time without conflicting with each other, you can use the `BatchProcessor` to process records asynchronously. This will create an array of promises that will be resolved once all records have been processed. - -```ts -import { - BatchProcessor, - EventType, - processPartialResponse, -} from '@aws-lambda-powertools/batch'; -import type { SQSHandler, SQSRecord } from 'aws-lambda'; - -const processor = new BatchProcessor(EventType.SQS); - -const recordHandler = async (record: SQSRecord): Promise => { - const res = await fetch('https://httpbin.org/anything', { - body: JSON.stringify({ message: record.body }), - }); - - return res.status; -}; - -export const handler: SQSHandler = async (event, context) => - await processPartialResponse(event, recordHandler, processor, { - context, - }); -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/batch/) for more examples. - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/commons/API_README.md b/packages/commons/API_README.md deleted file mode 100644 index 51e3a5d910..0000000000 --- a/packages/commons/API_README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the library in both TypeScript and JavaScript code bases. - -## Intro - -The Commons package contains a set of utilities that are shared across one or more Powertools for AWS Lambda (TypeScript) utilities. Some of these utilities can also be used independently in your AWS Lambda functions. - -## Usage - -To get started, install the utility by running: - -```sh -npm i @aws-lambda-powertools/commons -``` - -### Type utils - -When working with different objects and values, you may want to do runtime type checks. The utility comes with a set of type utilities that you can use to check the type of an object or value. - -```typescript -import { isRecord } from '@aws-lambda-powertools/commons/typeUtils'; -import { isString } from '@aws-lambda-powertools/commons/typeUtils'; -import { isTruthy } from '@aws-lambda-powertools/commons/typeUtils'; - - -const value = { key: 'value' }; -if (isRecord(value)) { - // value is a record -} - -const stringValue = 'string'; -if (isString(stringValue)) { - // stringValue is a string -} - -const truthyValue = 'true'; -if (isTruthy(truthyValue)) { - // truthyValue is truthy -} -``` - -You can find a full list of type utilities available [in the API docs](https://docs.aws.amazon.com/powertools/typescript/latest/api/modules/_aws-lambda-powertools_commons.typeUtils.html). Many of these utilities also double as type guards, which you can use to narrow down the type of an object or value. - -### Base64 utils - -When working with Base64-encoded data, you can use the `fromBase64` utilities to quickly decode data and convert it to a `Uint8Array`. - -```typescript - -import { fromBase64 } from '@aws-lambda-powertools/commons/utils/base64'; - -const encodedValue = 'aGVsbG8gd29ybGQ='; - -const decoded = fromBase64(encodedValue); -// new Uint8Array([ 97, 71, 86, 115, 98, 71, 56, 103, 100, 50, 57, 121, 98, 71, 81, 61 ]); -``` - -### JSON type utils - -In some cases, you may want to define a type for a JSON object or value. The utility comes with a set of types that you can use to define your JSON objects. - -```typescript -import type { JSONValue, JSONObject, JSONArray } from '@aws-lambda-powertools/commons'; -``` - -### Lambda interface - -When using object-oriented patterns to define your Lambda handlers, you can use the `LambdaHandler` interface to define the shape of your handler methods. - -```typescript -import type { Context } from 'aws-lambda'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; - -class Lambda implements LambdaInterface { - public handler = async (event: unknown, context: Context) => { - // Your handler code here - } -} - -const handlerClass = new Lambda(); -export const handler = lambda.handler.bind(lambda); -``` - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/event-handler/API_README.md b/packages/event-handler/API_README.md deleted file mode 100644 index 6dc0b8f75a..0000000000 --- a/packages/event-handler/API_README.md +++ /dev/null @@ -1,312 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - Event Handler Utility - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the library in both TypeScript and JavaScript code bases. - -## Intro - -Event handler for AWS AppSync GraphQL APIs, AWS AppSync Events APIs, and Amazon Bedrock Agent Functions. - -## Usage - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/event-handler -``` - -## AppSync Events - -Event Handler for AWS AppSync real-time events. - -* Easily handle publish and subscribe events with dedicated handler methods -* Automatic routing based on namespace and channel patterns -* Support for wildcard patterns to create catch-all handlers -* Process events in parallel corontrol aggregation for batch processing -* Graceful error handling for individual events - -### Handle publish events - -When using the publish event handler, you can register a handler for a specific channel or a wildcard pattern. The handler will be called once for each message received on that channel. - -```typescript -import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events'; - -const app = new AppSyncEventsResolver(); - -app.onPublish('/default/foo', async (payload) => { - // your logic here - return payload; -}); - -export const handler = async (event, context) => - app.resolve(event, context); -``` - -In some cases, you might want to process all the messages at once, for example to optimize downstream operations. In this case, you can set the `aggregate` option to `true` when registering the handler. This will cause the handler to be called once for all messages received on that channel. - -```typescript -import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events'; - -const app = new AppSyncEventsResolver(); - -app.onPublish('/default/foo', async (payloads) => { - const newMessages = []; - for (const message of payloads) { - // your logic here - } - - return newMessages; -}, { - aggregate: true -}); - -export const handler = async (event, context) => - app.resolve(event, context); -``` - -### Handle subscribe events - -You can also register a handler for subscribe events. This handler will be called once for each subscription request received on the specified channel. You can use this handler to perform any necessary setup or validation before allowing the subscription to proceed. - -```typescript -import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events'; - -const app = new AppSyncEventsResolver(); - -app.onSubscribe('/default/foo', async (event) => { - // your logic here -}); - -export const handler = async (event, context) => - app.resolve(event, context); -``` - -If you want to reject a subscription request, you can throw an `UnauthorizedException` error. This will cause the subscription to be rejected and the client will receive an error message. - -```typescript -import { - AppSyncEventsResolver, - UnauthorizedException, -} from '@aws-lambda-powertools/event-handler/appsync-events'; - -const app = new AppSyncEventsResolver(); - -app.onSubscribe('/default/foo', async (event) => { - // your logic here - throw new UnauthorizedException('Unauthorized'); -}); - -export const handler = async (event, context) => - app.resolve(event, context); -``` - -## AppSync GraphQL - -The Event Handler for AWS AppSync GraphQL APIs allows you to easily handle GraphQL requests in your Lambda functions. It enables you to define resolvers for GraphQL types and fields, making it easier to handle GraphQL requests without the need for complex VTL or JavaScript templates. - -* Route events based on GraphQL type and field keys -* Automatically parse API arguments to function parameters -* Handle GraphQL responses and errors in the expected format - -### Handle query requests - -When registering a resolver for a Query type, you can use the `onQuery()` method. This method allows you to define a function that will be invoked when a GraphQL Query is made. - -```typescript -import { Logger } from '@aws-lambda-powertools/logger'; -import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql'; -import type { Context } from 'aws-lambda'; - -const logger = new Logger({ - serviceName: 'TodoManager', -}); -const app = new AppSyncGraphQLResolver({ logger }); - -app.onQuery<{ id: string }>('getTodo', async ({ id }) => { - logger.debug('Resolving todo', { id }); - // Simulate fetching a todo from a database or external service - return { - id, - title: 'Todo Title', - completed: false, - }; -}); - -export const handler = async (event: unknown, context: Context) => - app.resolve(event, context); -``` - -### Handle mutation requests - -Similarly, you can register a resolver for a Mutation type using the `onMutation()` method. This method allows you to define a function that will be invoked when a GraphQL Mutation is made. - -```typescript -import { Logger } from '@aws-lambda-powertools/logger'; -import { - AppSyncGraphQLResolver, - makeId, -} from '@aws-lambda-powertools/event-handler/appsync-graphql'; -import type { Context } from 'aws-lambda'; - -const logger = new Logger({ - serviceName: 'TodoManager', -}); -const app = new AppSyncGraphQLResolver({ logger }); - -app.onMutation<{ title: string }>('createTodo', async ({ title }) => { - logger.debug('Creating todo', { title }); - const todoId = makeId(); - // Simulate creating a todo in a database or external service - return { - id: todoId, - title, - completed: false, - }; -}); - -export const handler = async (event: unknown, context: Context) => - app.resolve(event, context); -``` - -### Generic resolver - -When you want to have more control over the type and field, you can use the `resolver()` method. This method allows you to register a function for a specific GraphQL type and field including custom types. - -```typescript -import { Logger } from '@aws-lambda-powertools/logger'; -import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql'; -import type { Context } from 'aws-lambda'; - -const logger = new Logger({ - serviceName: 'TodoManager', -}); -const app = new AppSyncGraphQLResolver({ logger }); - -app.resolver( - async () => { - logger.debug('Resolving todos'); - // Simulate fetching a todo from a database or external service - return [ - { - id: 'todo-id', - title: 'Todo Title', - completed: false, - }, - { - id: 'todo-id-2', - title: 'Todo Title 2', - completed: true, - }, - ]; - }, - { - fieldName: 'listTodos', - typeName: 'Query', - } -); - -export const handler = async (event: unknown, context: Context) => - app.resolve(event, context); -``` - -See the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-events) for more details on how to use the AppSync event handler. - -## Bedrock Agent Functions - -Event Handler for Amazon Bedrock Agent Functions. - -* Easily expose tools for your Large Language Model (LLM) agents -* Automatic routing based on tool name and function details -* Graceful error handling and response formatting - -### Handle tool use - -When using the Bedrock Agent Functions event handler, you can register a handler for a specific tool name. The handler will be called when the agent uses that tool. - -```typescript -import { BedrockAgentFunctionResolver } from '@aws-lambda-powertools/event-handler/bedrock-agent'; -import type { Context } from 'aws-lambda'; - -const app = new BedrockAgentFunctionResolver(); - -app.tool<{ city: string }>( - async ({ city }) => { - // Simulate fetching weather data for the city - return { - city, - temperature: '20°C', - condition: 'Sunny', - }; - }, - { - name: 'getWeatherForCity', - description: 'Get weather for a specific city', // (1)! - } -); - -export const handler = async (event: unknown, context: Context) => - app.resolve(event, context); -``` - -You can also work with session attributes, which are key-value pairs that can be used to store information about the current session. The session attributes are automatically passed to the handler and can be used to store information that needs to be persisted across multiple tool invocations. - -```typescript -import { - BedrockAgentFunctionResolver, - BedrockFunctionResponse, -} from '@aws-lambda-powertools/event-handler/bedrock-agent'; -import type { Context } from 'aws-lambda'; - -const app = new BedrockAgentFunctionResolver(); - -app.tool<{ city: string }>( - async ({ city }, { event }) => { - const { - sessionAttributes, - promptSessionAttributes, - knowledgeBasesConfiguration, - } = event; - - // your logic to fetch weather data for the city - - return new BedrockFunctionResponse({ - body: JSON.stringify({ - city, - temperature: '20°C', - condition: 'Sunny', - }), - sessionAttributes: { - ...sessionAttributes, - isGoodWeather: true, - }, - promptSessionAttributes, - knowledgeBasesConfiguration, - }); - }, - { - name: 'getWeatherForCity', - description: 'Get weather for a specific city', - } -); - -export const handler = async (event: unknown, context: Context) => - app.resolve(event, context); -``` - -See the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/event-handler/appsync-events) for more details on how to use the AppSync event handler. - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -* **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -* **Email**: diff --git a/packages/idempotency/API_README.md b/packages/idempotency/API_README.md deleted file mode 100644 index 5d49cbc890..0000000000 --- a/packages/idempotency/API_README.md +++ /dev/null @@ -1,324 +0,0 @@ - -# Powertools for AWS Lambda (TypeScript) - Idempotency Utility - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement -Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the package in both TypeScript and JavaScript code bases. - -- [Intro](#intro) -- [Usage](#usage) - - [Function wrapper](#function-wrapper) - - [Decorator](#decorator) - - [Middy middleware](#middy-middleware) - - [DynamoDB persistence layer](#dynamodb-persistence-layer) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Intro - -This package provides a utility to implement idempotency in your Lambda functions. -You can either use it to wrap a function, decorate a method, or as Middy middleware to make your AWS Lambda handler -idempotent. - -The current implementation provides a persistence layer for Amazon DynamoDB, which offers a variety of configuration -options. You can also bring your own persistence layer by extending the `BasePersistenceLayer` class. - -## Usage - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/idempotency @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb -``` - -Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow -the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/#iam-permissions) -in the documentation of the utility. - -### Function wrapper - -You can make any function idempotent, and safe to retry, by wrapping it using the `makeIdempotent` higher-order -function. - -The `makeIdempotent` function takes a reference to the function to be made idempotent as first argument, and an object -with options as second argument. - -When you wrap your Lambda handler function, the utility uses the content of the `event` parameter to handle the -idempotency logic. - -```ts -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: 'idempotencyTableName', -}); - -const myHandler = async ( - event: APIGatewayProxyEvent, - _context: Context -): Promise => { - // your code goes here here -}; - -export const handler = makeIdempotent(myHandler, { - persistenceStore, -}); -``` - -You can also use the `makeIdempotent` function to wrap any other arbitrary function, not just Lambda handlers. - -```ts -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: 'idempotencyTableName', -}); - -const processingFunction = async (payload: SQSRecord): Promise => { - // your code goes here here -}; - -const processIdempotently = makeIdempotent(processingFunction, { - persistenceStore, -}); - -export const handler = async ( - event: SQSEvent, - _context: Context -): Promise => { - for (const record of event.Records) { - await processIdempotently(record); - } -}; -``` - -If your function has multiple arguments, you can use the `dataIndexArgument` option to specify which argument should be -used as the idempotency key. - -```ts -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: 'idempotencyTableName', -}); - -const processingFunction = async (payload: SQSRecord, customerId: string): Promise => { - // your code goes here here -}; - -const processIdempotently = makeIdempotent(processingFunction, { - persistenceStore, - // this tells the utility to use the second argument (`customerId`) as the idempotency key - dataIndexArgument: 1, -}); - -export const handler = async ( - event: SQSEvent, - _context: Context -): Promise => { - for (const record of event.Records) { - await processIdempotently(record, 'customer-123'); - } -}; -``` - -Note that you can also specify a JMESPath expression in the Idempotency config object to select a subset of the event -payload as the idempotency key. This is useful when dealing with payloads that contain timestamps or request ids. - -```ts -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: 'idempotencyTableName', -}); - -const myHandler = async ( - event: APIGatewayProxyEvent, - _context: Context -): Promise => { - // your code goes here here -}; - -export const handler = makeIdempotent(myHandler, { - persistenceStore, - config: new IdempotencyConfig({ - eventKeyJmespath: 'requestContext.identity.user', - }), -}); -``` - -Additionally, you can also use one of -the [JMESPath built-in functions](https://docs.aws.amazon.com/powertools/typescript/latest/features/jmespath/#built-in-jmespath-functions) -like `powertools_json()` to decode keys and use parts of the payload as the idempotency key. - -```ts -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: 'idempotencyTableName', -}); - -const myHandler = async ( - event: APIGatewayProxyEvent, - _context: Context -): Promise => { - // your code goes here here -}; - -export const handler = makeIdempotent(myHandler, { - persistenceStore, - config: new IdempotencyConfig({ - eventKeyJmespath: 'powertools_json(body).["user", "productId"]', - }), -}); -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/) for more examples. - -### Decorator - -You can make any function idempotent, and safe to retry, by decorating it using the `@idempotent` decorator. - -```ts -import {idempotent} from '@aws-lambda-powertools/idempotency'; -import {LambdaInterface} from '@aws-lambda-powertools/commons'; -import {DynamoDBPersistenceLayer} from '@aws-lambda-powertools/idempotency/dynamodb'; -import type {Context, APIGatewayProxyEvent} from 'aws-lambda'; - -const persistenceStore = new DynamoDBPersistenceLayer({ - tableName: 'idempotencyTableName', -}); - -class MyHandler extends LambdaInterface { - @idempotent({persistenceStore: dynamoDBPersistenceLayer}) - public async handler( - event: APIGatewayProxyEvent, - context: Context - ): Promise { - // your code goes here here - } -} - -const handlerClass = new MyHandler(); -export const handler = handlerClass.handler.bind(handlerClass); -``` - -Using the same decorator, you can also make any other arbitrary method idempotent. - -```ts -import {idempotent} from '@aws-lambda-powertools/idempotency'; -import {LambdaInterface} from '@aws-lambda-powertools/commons'; -import {DynamoDBPersistenceLayer} from '@aws-lambda-powertools/idempotency/dynamodb'; -import type {Context} from 'aws-lambda'; - -const persistenceStore = new DynamoDBPersistenceLayer({ - tableName: 'idempotencyTableName', -}); - -class MyHandler extends LambdaInterface { - - public async handler( - event: unknown, - context: Context - ): Promise { - for (const record of event.Records) { - await this.processIdempotently(record); - } - } - - @idempotent({persistenceStore: dynamoDBPersistenceLayer}) - private async process(record: unknown): Promise { - // process each code idempotently - } -} - -const handlerClass = new MyHandler(); -export const handler = handlerClass.handler.bind(handlerClass); -``` - -The decorator configuration options are identical with the ones of the `makeIdempotent` function. Check -the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/) for more examples. - -### Middy middleware - -If instead you use Middy, you can use the `makeHandlerIdempotent` middleware. When using the middleware your Lambda -handler becomes idempotent. - -By default, the Idempotency utility will use the full event payload to create an hash and determine if a request is -idempotent, and therefore it should not be retried. -When dealing with a more elaborate payload, where parts of the payload always change you should use -the `IdempotencyConfig` object to instruct the utility to only use a portion of your payload. This is useful when -dealing with payloads that contain timestamps or request ids. - -```ts -import {IdempotencyConfig} from '@aws-lambda-powertools/idempotency'; -import {makeHandlerIdempotent} from '@aws-lambda-powertools/idempotency/middleware'; -import {DynamoDBPersistenceLayer} from '@aws-lambda-powertools/idempotency/dynamodb'; -import middy from '@middy/core'; -import type {Context, APIGatewayProxyEvent} from 'aws-lambda'; - -const persistenceStore = new DynamoDBPersistenceLayer({ - tableName: 'idempotencyTableName', -}); -const config = new IdempotencyConfig({ - hashFunction: 'md5', - useLocalCache: false, - expiresAfterSeconds: 3600, - throwOnNoIdempotencyKey: false, - eventKeyJmesPath: 'headers.idempotency-key', -}); - -export const handler = middy( - async (_event: APIGatewayProxyEvent, _context: Context): Promise => { - // your code goes here here - } -).use( - makeHandlerIdempotent({ - config, - persistenceStore, - }) -); -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/idempotency/) for more examples. - -### DynamoDB persistence layer - -You can use a DynamoDB Table to store the idempotency information. This enables you to keep track of the hash key, -payload, status for progress, expiration, and much more. - -You can customize most of the configuration options of the table, i.e the names of the attributes. -See -the [API documentation](https://docs.aws.amazon.com/powertools/typescript/latest/api/types/_aws-lambda-powertools_idempotency.types.DynamoDBPersistenceOptions.html) -for more details. - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/jmespath/API_README.md b/packages/jmespath/API_README.md deleted file mode 100644 index baa3e62c0b..0000000000 --- a/packages/jmespath/API_README.md +++ /dev/null @@ -1,185 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - JMESPath Utility - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the package in both TypeScript and JavaScript code bases. - -- [Intro](#intro) -- [Usage](#usage) - - [Basic usage](#basic-usage) - - [Extract data from envelopes](#extract-data-from-envelopes) - - [JMESPath custom functions](#jmespath-custom-functions) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Intro - -The JMESPath utility is a high-level function to parse and extract data from JSON objects using JMESPath expressions. - -## Usage - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/jmespath -``` - -### Basic usage - -At its core, the library provides a utility function to extract data from a JSON object using a JMESPath expression. - -```ts -import { search } from '@aws-lambda-powertools/jmespath'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -type MyEvent = { - foo: { - bar: string; - }; -} - -export const handler = async (event: MyEvent): Promise => { - const result = search(event, 'foo.bar'); - logger.info(result); // "baz" -}; -``` - -### Extract data from envelopes - -In some cases, you may want to extract data from an envelope. The library provides a utility function to help you work with envelopes and extract data from them. - -```ts -import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; - -type MyEvent = { - body: string; // "{\"customerId\":\"dd4649e6-2484-4993-acb8-0f9123103394\"}" - deeplyNested: Array<{ someData: number[] }>; -}; - -type MessageBody = { - customerId: string; -}; - -export const handler = async (event: MyEvent): Promise => { - const payload = extractDataFromEnvelope( - event, - 'powertools_json(body)' - ); - const { customerId } = payload; // now deserialized - - // also works for fetching and flattening deeply nested data - const someData = extractDataFromEnvelope( - event, - 'deeplyNested[*].someData[]' - ); - - return { - customerId, - message: 'success', - context: someData, - statusCode: 200, - }; -}; -``` - -The library provides [a set of built-in envelopes](https://docs.aws.amazon.com/powertools/typescript/latest/features/jmespath/#built-in-envelopes) to help you extract data from common event sources, such as S3, SQS, and SNS, and more. - -```ts -import { - extractDataFromEnvelope, - SQS, -} from '@aws-lambda-powertools/jmespath/envelopes'; -import { Logger } from '@aws-lambda-powertools/logger'; -import type { SQSEvent } from 'aws-lambda'; - -const logger = new Logger(); - -type MessageBody = { - customerId: string; -}; - -export const handler = async (event: SQSEvent): Promise => { - const records = extractDataFromEnvelope>(event, SQS); - for (const record of records) { - // records is now a list containing the deserialized body of each message - const { customerId } = record; - logger.appendKeys({ customerId }); - } -}; -``` - -### JMESPath custom functions - -In addition to all the [built-in JMESPath functions](https://jmespath.org/specification.html#built-in-functions), the library provides custom functions to help you work with complex data structures. For example, you can use the `powertools_json` function to parse a JSON string, or the `powertools_base64` function to decode a base64-encoded string: - -```ts -import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -export const handler = async (event: { payload: string }): Promise => { - const data = extractDataFromEnvelope( - event, - 'powertools_json(powertools_base64(payload))' - ); - - logger.info('Decoded payload', { data }); -}; -``` - -Finally, you can also extend the library with your own custom functions. Below an example of how to create a custom function to decode a Brotli-compressed string. - -```ts -import { fromBase64 } from '@aws-lambda-powertools/commons/utils/base64'; -import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; -import { PowertoolsFunctions } from '@aws-lambda-powertools/jmespath/functions'; -import { Logger } from '@aws-lambda-powertools/logger'; -import { brotliDecompressSync } from 'node:zlib'; - -const logger = new Logger(); - -class CustomFunctions extends PowertoolsFunctions { - @PowertoolsFunctions.signature({ - argumentsSpecs: [['string']], - variadic: false, - }) - public funcDecodeBrotliCompression(value: string): string { - const encoded = fromBase64(value, 'base64'); - const uncompressed = brotliDecompressSync(encoded); - - return uncompressed.toString(); - } -} - -export const handler = async (event: { payload: string }): Promise => { - const message = extractDataFromEnvelope( - event, - 'Records[*].decode_brotli_compression(notification) | [*].powertools_json(@).message', - { customFunctions: new CustomFunctions() } - ); - - logger.info('Decoded message', { message }); -}; -``` - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/kafka/API_README.md b/packages/kafka/API_README.md deleted file mode 100644 index 851f572505..0000000000 --- a/packages/kafka/API_README.md +++ /dev/null @@ -1,265 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - Kafka Utility - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the package in both TypeScript and JavaScript code bases. - -## Intro - -The Kafka Consumer utility transparently handles message deserialization, provides an intuitive developer experience, and integrates seamlessly with the rest of the Powertools for AWS Lambda ecosystem. - -## Usage - -To get started, depending on the schema types you want to use, install the library and the corresponding libraries: - -For JSON schemas: - -```bash -npm install @aws-lambda-powertools/kafka -``` - -For Avro schemas: - -```bash -npm install @aws-lambda-powertools/kafka avro-js -``` - -For Protobuf schemas: - -```bash -npm install @aws-lambda-powertools/kafka protobufjs -``` - -Additionally, if you want to use output parsing with [Standard Schema](https://github.com/standard-schema/standard-schema), you can install [any of the supported libraries](https://standardschema.dev/#what-schema-libraries-implement-the-spec), for example: Zod, Valibot, or ArkType. - -### Deserialization - -The Kafka consumer utility transforms raw Kafka events into an intuitive format for processing. To handle messages effectively, you'll need to configure a schema that matches your data format. - -#### JSON Schema - -```ts -import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; -import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger({ serviceName: 'kafka-consumer' }); - -const schemaConfig = { - value: { - type: SchemaType.JSON, - }, -} satisfies SchemaConfig; - -export const handler = kafkaConsumer(async (event, _context) => { - for (const { value } of event.records) { - logger.info('received value', { value }); - } -}, schemaConfig); -``` - -#### Avro Schema - -```ts -import { readFileSync } from 'node:fs'; -import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; -import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger({ serviceName: 'kafka-consumer' }); - -const schemaConfig = { - value: { - type: SchemaType.AVRO, - schema: readFileSync(new URL('./user.avsc', import.meta.url), 'utf8'), - }, -} satisfies SchemaConfig; - -export const handler = kafkaConsumer(async (event, _context) => { - for (const { value } of event.records) { - logger.info('received value', { value }); - } -}, schemaConfig); -``` - -#### Protobuf Schema - -```ts -import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; -import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; -import { Logger } from '@aws-lambda-powertools/logger'; -import { User } from './samples/user.es6.generated.js'; // protobuf generated class - -const logger = new Logger({ serviceName: 'kafka-consumer' }); - -const schemaConfig = { - value: { - type: SchemaType.PROTOBUF, - schema: User, - }, -} satisfies SchemaConfig; - -export const handler = kafkaConsumer(async (event, _context) => { - for (const { value } of event.records) { - logger.info('received value', { value }); - } -}, schemaConfig); -``` - -### Additional Parsing - -You can parse deserialized data using your preferred parsing library. This can help you integrate Kafka data with your domain schemas and application architecture, providing type hints, runtime parsing and validation, and advanced data transformations. - -#### Zod - -```ts -import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; -import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; -import { Logger } from '@aws-lambda-powertools/logger'; -import { z } from 'zod/v4'; - -const logger = new Logger({ serviceName: 'kafka-consumer' }); - -const OrderItemSchema = z.object({ - productId: z.string(), - quantity: z.number().int().positive(), - price: z.number().positive(), -}); - -const OrderSchema = z.object({ - id: z.string(), - customerId: z.string(), - items: z.array(OrderItemSchema).min(1, 'Order must have at least one item'), - createdAt: z.iso.datetime(), - totalAmount: z.number().positive(), -}); - -const schemaConfig = { - value: { - type: SchemaType.JSON, - parserSchema: OrderSchema, - }, -} satisfies SchemaConfig; - -export const handler = kafkaConsumer>( - async (event, _context) => { - for (const record of event.records) { - const { - value: { id, items }, - } = record; - logger.setCorrelationId(id); - logger.debug(`order includes ${items.length} items`); - } - }, - schemaConfig -); -``` - -#### Valibot - -```ts -import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; -import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; -import { Logger } from '@aws-lambda-powertools/logger'; -import * as v from 'valibot'; - -const logger = new Logger({ serviceName: 'kafka-consumer' }); - -const OrderItemSchema = v.object({ - productId: v.string(), - quantity: v.pipe(v.number(), v.integer(), v.toMinValue(1)), - price: v.pipe(v.number(), v.integer()), -}); - -const OrderSchema = v.object({ - id: v.string(), - customerId: v.string(), - items: v.pipe( - v.array(OrderItemSchema), - v.minLength(1, 'Order must have at least one item') - ), - createdAt: v.pipe(v.string(), v.isoDateTime()), - totalAmount: v.pipe(v.number(), v.toMinValue(0)), -}); - -const schemaConfig = { - value: { - type: SchemaType.JSON, - parserSchema: OrderSchema, - }, -} satisfies SchemaConfig; - -export const handler = kafkaConsumer>( - async (event, _context) => { - for (const record of event.records) { - const { - value: { id, items }, - } = record; - logger.setCorrelationId(id); - logger.debug(`order includes ${items.length} items`); - } - }, - schemaConfig -); -``` - -#### ArkType - -```ts -import { SchemaType, kafkaConsumer } from '@aws-lambda-powertools/kafka'; -import type { SchemaConfig } from '@aws-lambda-powertools/kafka/types'; -import { Logger } from '@aws-lambda-powertools/logger'; -import { type } from 'arktype'; - -const logger = new Logger({ serviceName: 'kafka-consumer' }); - -const OrderItemSchema = type({ - productId: 'string', - quantity: 'number.integer >= 1', - price: 'number.integer', -}); - -const OrderSchema = type({ - id: 'string', - customerId: 'string', - items: OrderItemSchema.array().moreThanLength(0), - createdAt: 'string.date', - totalAmount: 'number.integer >= 0', -}); - -const schemaConfig = { - value: { - type: SchemaType.JSON, - parserSchema: OrderSchema, - }, -} satisfies SchemaConfig; - -export const handler = kafkaConsumer( - async (event, _context) => { - for (const record of event.records) { - const { - value: { id, items }, - } = record; - logger.setCorrelationId(id); - logger.debug(`order includes ${items.length} items`); - } - }, - schemaConfig -); -``` - -See the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/kafka) for more details on how to use the Kafka utility. - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -* **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -* **Email**: diff --git a/packages/logger/API_README.md b/packages/logger/API_README.md deleted file mode 100644 index ab0fc6e779..0000000000 --- a/packages/logger/API_README.md +++ /dev/null @@ -1,219 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the library in both TypeScript and JavaScript code bases. - -- [Intro](#intro) -- [Usage](#usage) - - [Basic usage](#basic-usage) - - [Inject Lambda context](#inject-lambda-context) - - [Logging incoming event](#logging-incoming-event) - - [Append additional keys and data](#append-additional-keys-and-data) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Intro - -The Logger utility provides a structured logging experience with additional features tailored for AWS Lambda functions. - -## Usage - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/logger -``` - -### Basic usage - -Initialize the logger with a service name and log messages: - -```ts -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger({ serviceName: 'serverlessAirline' }); - -export const handler = async (_event, _context): Promise => { - logger.info('Hello World'); - -}; -``` - -You can also log errors and additional data: - -```ts -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -export const handler = async ( - _event: unknown, - _context: unknown -): Promise => { - try { - throw new Error('Unexpected error #1'); - } catch (error) { - // Log information about the error using the default "error" key - logger.error('This is the first error', error as Error); - } - - try { - throw new Error('Unexpected error #2'); - } catch (error) { - // Log information about the error using a custom "myCustomErrorKey" key - logger.error('This is the second error', { - myCustomErrorKey: error as Error, - }); - } -}; -``` - -### Inject Lambda context - -You can enrich your structured logs with key Lambda context information: - -```ts -import { Logger } from '@aws-lambda-powertools/logger'; -import type { Context } from 'aws-lambda'; - -const logger = new Logger(); - -export const handler = async ( - _event: unknown, - context: Context -): Promise => { - logger.addContext(context); - - logger.info('This is an INFO log with some context'); -}; -``` - -The log statement will look like this: - -```json -{ - "cold_start": true, - "function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:shopping-cart-api-lambda-prod-eu-west-1", - "function_memory_size": 128, - "function_request_id": "c6af9ac6-7b61-11e6-9a41-93e812345678", - "function_name": "shopping-cart-api-lambda-prod-eu-west-1", - "level": "INFO", - "message": "This is an INFO log with some context", - "service": "serverlessAirline", - "timestamp": "2021-12-12T21:21:08.921Z", - "xray_trace_id": "abcdef123456abcdef123456abcdef123456" -} -``` - -### Logging incoming event - -You can log the incoming event with (here as decorator, works also as middy middleware): - -```ts -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -class Lambda implements LambdaInterface { - // Set the log event flag to true - @logger.injectLambdaContext({ logEvent: true }) - public async handler(_event: unknown, _context: unknown): Promise { - logger.info('This is an INFO log with some context'); - } -} - -const myFunction = new Lambda(); -export const handler = myFunction.handler.bind(myFunction); // -``` - -### Append additional keys and data - -Append additional keys: - -```ts -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -export const handler = async ( - event: unknown, - _context: unknown -): Promise => { - const myImportantVariable = { - foo: 'bar', - }; - - // Log additional data in single log items - // As second parameter - logger.info('This is a log with an extra variable', { - data: myImportantVariable, - }); - - // You can also pass multiple parameters containing arbitrary objects - logger.info( - 'This is a log with 3 extra objects', - { data: myImportantVariable }, - { correlationIds: { myCustomCorrelationId: 'foo-bar-baz' } }, - { lambdaEvent: event } - ); - - // Simply pass a string for logging additional data - logger.info('This is a log with additional string value', 'string value'); - - // Directly passing an object containing both the message and the additional info - const logObject = { - message: 'This is a log message', - additionalValue: 42, - }; - - logger.info(logObject); - - return { - foo: 'bar', - }; -}; -``` - -Add **persistent keys** to the logger instance: - -```ts -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger({ - serviceName: 'serverlessAirline', - persistentKeys: { - environment: 'prod', - version: process.env.BUILD_VERSION, - }, -}); - -export const handler = async ( - _event: unknown, - _context: unknown -): Promise => { - logger.info('processing transaction'); - - // ... your business logic -}; -``` - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/metrics/API_README.md b/packages/metrics/API_README.md deleted file mode 100644 index 94593f8359..0000000000 --- a/packages/metrics/API_README.md +++ /dev/null @@ -1,144 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the library in both TypeScript and JavaScript code bases. - -- [Usage](#usage) - - [Flushing metrics](#flushing-metrics) - - [Capturing cold start as a metric](#capturing-cold-start-as-a-metric) - - [Class method decorator](#class-method-decorator) - - [Middy.js middleware](#middyjs-middleware) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Usage - -The library provides a utility function to emit metrics to CloudWatch using [Embedded Metric Format (EMF)](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html). - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/metrics -``` - -After initializing the Metrics class, you can add metrics using the [https://docs.aws.amazon.com/powertools/typescript/latest/core/metrics/#creating-metrics](`addMetric()`) method. The metrics are stored in a buffer and are flushed when calling [https://docs.aws.amazon.com/powertools/typescript/latest/core/metrics/#flushing-metrics](`publishStoredMetrics()`). - -Each metric can also have dimensions and metadata added to it. - -```ts -import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; - -const metrics = new Metrics({ - namespace: 'serverlessAirline', - serviceName: 'orders', - defaultDimensions: { environment: process.env.ENVIRONMENT }, -}); - -export const handler = async (event: { requestId: string }) => { - metrics.addMetadata('request_id', event.requestId); - metrics.addMetric('successfulBooking', MetricUnit.Count, 1); - metrics.publishStoredMetrics(); -}; -``` - -### Flushing metrics - -As you finish adding all your metrics, you need to serialize and "flush them" by calling `publishStoredMetrics()`, which will emit the metrics to stdout in the Embedded Metric Format (EMF). The metrics are then picked up by the Lambda runtime and sent to CloudWatch. - -The `publishStoredMetrics()` method is synchronous and will block the event loop until the metrics are flushed. If you want Metrics to flush automatically at the end of your Lambda function, you can use the `@logMetrics()` decorator or the `logMetrics()` middleware. - -### Capturing cold start as a metric - -With Metrics, you can capture cold start as a metric by calling the `captureColdStartMetric()` method. This method will add a metric with the name `ColdStart` and the value `1` to the metrics buffer. - -This metric is flushed automatically as soon as the method is called, to ensure that the cold start is captured regardless of whether the metrics are flushed manually or automatically. - -```ts -import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; - -const metrics = new Metrics({ - namespace: 'serverlessAirline', - serviceName: 'orders', -}); - -export const handler = async (event: { requestId: string }) => { - metrics.captureColdStartMetric(); -}; -``` - -Note that we don't emit a `ColdStart` metric with value `0` when the function is warm, as this would result in a high volume of metrics being emitted to CloudWatch, so you'll need to rely on the absence of the `ColdStart` metric to determine if the function is warm. - -### Class method decorator - -If you are using TypeScript and are comfortable with writing classes, you can use the `@logMetrics()` decorator to automatically flush metrics at the end of your Lambda function as well as configure additional options such as throwing an error if no metrics are added, capturing cold start as a metric, and more. - -```ts -import type { Context } from 'aws-lambda'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; - -const metrics = new Metrics({ - namespace: 'serverlessAirline', - serviceName: 'orders', -}); - -class Lambda implements LambdaInterface { - ⁣@metrics.logMetrics({ captureColdStartMetric: true, throwOnEmptyMetrics: true }) - public async handler(event: { requestId: string }, _: Context) { - metrics.addMetadata('request_id', event.requestId); - metrics.addMetric('successfulBooking', MetricUnit.Count, 1); - } -} - -const handlerClass = new Lambda(); -export const handler = handlerClass.handler.bind(handlerClass); -``` - -Decorators are a Stage 3 proposal for JavaScript and are not yet part of the ECMAScript standard. The current implmementation in this library is based on the legacy TypeScript decorator syntax enabled by the [`experimentalDecorators` flag](https://www.typescriptlang.org/tsconfig/#experimentalDecorators) set to `true` in the `tsconfig.json` file. - -### Middy.js middleware - -If instead you are using [Middy.js](http://middy.js.org) and prefer to use middleware, you can use the `@logMetrics()` middleware to do the same as the class method decorator. - -The `@logMetrics()` middleware can be used with Middy.js to automatically flush metrics at the end of your Lambda function as well as configure additional options such as throwing an error if no metrics are added, capturing cold start as a metric, and set default dimensions. - -```ts -import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; -import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; -import middy from '@middy/core'; - -const metrics = new Metrics({ - namespace: 'serverlessAirline', - serviceName: 'orders', -}); - -export const handler = middy(async (event) => { - metrics.addMetadata('request_id', event.requestId); - metrics.addMetric('successfulBooking', MetricUnit.Count, 1); -}).use(logMetrics(metrics, { - captureColdStartMetric: true, - throwOnEmptyMetrics: true, -})); -``` - -The `logMetrics()` middleware is compatible with `@middy/core@3.x` and above. - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/parameters/API_README.md b/packages/parameters/API_README.md deleted file mode 100644 index 6ada8a66df..0000000000 --- a/packages/parameters/API_README.md +++ /dev/null @@ -1,209 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the package in both TypeScript and JavaScript code bases. - -- [Intro](#intro) -- [Usage](#usage) - - [Fetching parameters from AWS SSM Parameter Store](#fetching-parameters-from-aws-ssm-parameter-store) - - [Getting secrets from Amazon Secrets Manager](#getting-secrets-from-amazon-secrets-manager) - - [Retrieving values from Amazon DynamoDB](#retrieving-values-from-amazon-dynamodb) - - [Fetching configs from AWS AppConfig](#fetching-configs-from-aws-appconfig) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Intro - -The Parameters utility provides high-level functions to retrieve one or multiple parameter values from - -- [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) -- [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html), -- [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html), -- [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html), -- or your own parameter store. - -## Usage - -### Fetching parameters from AWS SSM Parameter Store - -To get started, install the library and the corresponding AWS SDK for JavaScript v3: - -```sh -npm install @aws-lambda-powertools/parameters @aws-sdk/client-ssm -``` - -Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. - -You can retrieve a single parameter using the `getParameter` high-level function. - -```ts -import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; - -export const handler = async (): Promise => { - // Retrieve a single parameter - const parameter = await getParameter('/my/parameter'); - console.log(parameter); -}; -``` - -For multiple parameters, you can use `getParameters` to recursively fetch all parameters under a path: - -```ts -import { getParameters } from '@aws-lambda-powertools/parameters/ssm'; - -export const handler = async (): Promise => { - /** - * Retrieve multiple parameters from a path prefix recursively. - * This returns an object with the parameter name as key - */ - const parameters = await getParameters('/my/path/prefix'); - for (const [key, value] of Object.entries(parameters || {})) { - console.log(`${key}: ${value}`); - } -}; -``` - -To fetch distinct parameters using their full name, you can use the `getParametersByName` function: - -```ts -import { Transform } from '@aws-lambda-powertools/parameters'; -import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; -import type { SSMGetParametersByNameOptions } from '@aws-lambda-powertools/parameters/ssm/types'; - -const props: Record = { - '/develop/service/commons/telemetry/config': { - maxAge: 300, - transform: Transform.JSON, - }, - '/no_cache_param': { maxAge: 0 }, - '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values -}; - -export const handler = async (): Promise => { - // This returns an object with the parameter name as key - const parameters = await getParametersByName(props, { maxAge: 60 }); - for (const [key, value] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } -}; -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-parameters) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. - -### Getting secrets from Amazon Secrets Manager - -To get started, install the library and the corresponding AWS SDK for JavaScript v3: - -```sh -npm install @aws-lambda-powertools/parameters @aws-sdk/client-secrets-manager -``` - -Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. - -You can fetch secrets stored in Secrets Manager using the `getSecret` function: - -```ts -import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; - -export const handler = async (): Promise => { - // Retrieve a single secret - const secret = await getSecret('my-secret'); - console.log(secret); -}; -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-secrets) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. - -### Retrieving values from Amazon DynamoDB - -To get started, install the library and the corresponding AWS SDK for JavaScript v3: - -```sh -npm install @aws-lambda-powertools/parameters @aws-sdk/client-dynamodb @aws-sdk/util-dynamodb -``` - -Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. - -You can retrieve a single parameter from DynamoDB using the `DynamoDBProvider.get()` method: - -```ts -import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - -const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); - -export const handler = async (): Promise => { - // Retrieve a value from DynamoDB - const value = await dynamoDBProvider.get('my-parameter'); - console.log(value); -}; -``` - -For retrieving multiple parameters, you can use the `DynamoDBProvider.getMultiple()` method instead: - -```ts -import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - -const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); - -export const handler = async (): Promise => { - /** - * Retrieve multiple values by performing a Query on the DynamoDB table. - * This returns a dict with the sort key attribute as dict key. - */ - const values = await dynamoDBProvider.getMultiple('my-hash-key'); - for (const [key, value] of Object.entries(values || {})) { - // key: param-a - // value: my-value-a - console.log(`${key}: ${value}`); - } -}; -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-secrets) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. - -### Fetching configs from AWS AppConfig - -To get started, install the library and the corresponding AWS SDK for JavaScript v3: - -```sh -npm install @aws-lambda-powertools/parameters @aws-sdk/client-appconfigdata -``` - -Next, review the IAM permissions attached to your AWS Lambda function and make sure you allow the [actions detailed](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#iam-permissions) in the documentation of the utility. - -You can fetch application configurations in AWS AppConfig using the `getAppConfig` function: - -```ts -import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig'; - -export const handler = async (): Promise => { - // Retrieve a configuration, latest version - const config = await getAppConfig('my-configuration', { - environment: 'my-env', - application: 'my-app', - }); - console.log(config); -}; -``` - -Check the [docs](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#fetching-app-configurations) for more examples, and [the advanced section](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters/#advanced) for details about caching, transforms, customizing the underlying SDK, and more. - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/parser/API_README.md b/packages/parser/API_README.md deleted file mode 100644 index 29ad75b720..0000000000 --- a/packages/parser/API_README.md +++ /dev/null @@ -1,314 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - Parser Utility - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the package in both TypeScript and JavaScript code bases. - -- [Intro](#intro) -- [Key features](#key-features) -- [Usage](#usage) - - [Middy.js Middleware](#middyjs-middleware) - - [Decorator](#decorator) - - [Manual parsing](#manual-parsing) - - [Safe parsing](#safe-parsing) - - [Built-in schemas and envelopes](#built-in-schemas-and-envelopes) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Intro - -This utility provides data validation and parsing for [Standard Schema](https://github.com/standard-schema/standard-schema), together with a collection of built-in [Zod](https://zod.dev) schemas and envelopes to parse and unwrap popular AWS event source payloads. - -## Key features - -- Accept a [Standard Schema](https://github.com/standard-schema/standard-schema) and parse incoming payloads -- Built-in Zod schemas and envelopes to unwrap and validate popular AWS event sources payloads -- Extend and customize built-in Zod schemas to fit your needs -- Safe parsing option to avoid throwing errors and allow custom error handling -- Available as Middy.js middleware and TypeScript class method decorator - -## Usage - -To get started, install the library by running: - -```sh -npm install @aws-lambda-powertools/parser zod -``` - -You can parse inbound events using the `parser` decorator, Middy.js middleware, or [manually](#manual-parsing) using built-in envelopes and schemas. - -When using the decorator or middleware, you can specify a schema to parse the event: this can be a [built-in Zod schema](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser/#built-in-schemas) or a custom schema you defined. Custom schemas can be defined using Zod or any other [Standard Schema compatible library](https://standardschema.dev/#what-schema-libraries-implement-the-spec). - -### Middy.js Middleware - -Using Zod schemas: - -```typescript -import { Logger } from '@aws-lambda-powertools/logger'; -import { parser } from '@aws-lambda-powertools/parser/middleware'; -import middy from '@middy/core'; -import { z } from 'zod'; - -const logger = new Logger(); - -const orderSchema = z.object({ - id: z.number().positive(), - description: z.string(), - items: z.array( - z.object({ - id: z.number().positive(), - quantity: z.number().positive(), - description: z.string(), - }) - ), - optionalField: z.string().optional(), -}); - -export const handler = middy() - .use(parser({ schema: orderSchema })) - .handler(async (event): Promise => { - for (const item of event.items) { - // item is parsed as OrderItem - logger.info('Processing item', { item }); - } - }); -``` - -Using Valibot schemas: - -```typescript -import { Logger } from '@aws-lambda-powertools/logger'; -import { parser } from '@aws-lambda-powertools/parser/middleware'; -import middy from '@middy/core'; -import { - array, - number, - object, - optional, - pipe, - string, - toMinValue, -} from 'valibot'; - -const logger = new Logger(); - -const orderSchema = object({ - id: pipe(number(), toMinValue(0)), - description: string(), - items: array( - object({ - id: pipe(number(), toMinValue(0)), - quantity: pipe(number(), toMinValue(1)), - description: string(), - }) - ), - optionalField: optional(string()), -}); - -export const handler = middy() - .use(parser({ schema: orderSchema })) - .handler(async (event): Promise => { - for (const item of event.items) { - // item is parsed as OrderItem - logger.info('Processing item', { item }); - } - }); -``` - -### Decorator - -```typescript -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { Logger } from '@aws-lambda-powertools/logger'; -import { parser } from '@aws-lambda-powertools/parser'; -import type { Context } from 'aws-lambda'; -import { z } from 'zod'; - -const logger = new Logger(); - -const orderSchema = z.object({ - id: z.number().positive(), - description: z.string(), - items: z.array( - z.object({ - id: z.number().positive(), - quantity: z.number(), - description: z.string(), - }) - ), - optionalField: z.string().optional(), -}); - -type Order = z.infer; - -class Lambda implements LambdaInterface { - @parser({ schema: orderSchema }) - public async handler(event: Order, _context: Context): Promise { - // event is now typed as Order - for (const item of event.items) { - logger.info('Processing item', { item }); - } - } -} - -const myFunction = new Lambda(); -export const handler = myFunction.handler.bind(myFunction); -``` - -### Manual parsing - -If you don't want to add an additional middleware dependency, or you prefer the manual approach, you can parse the event directly by calling the `parse` method on schemas and envelopes: - -```typescript -import type { Context } from 'aws-lambda'; -import { z } from 'zod'; -import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes'; -import { EventBridgeSchema } from '@aws-lambda-powertools/parser/schemas'; -import type { EventBridgeEvent } from '@aws-lambda-powertools/parser/types'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -const orderSchema = z.object({ - id: z.number().positive(), - description: z.string(), - items: z.array( - z.object({ - id: z.number().positive(), - quantity: z.number(), - description: z.string(), - }) - ), - optionalField: z.string().optional(), -}); -type Order = z.infer; - -export const handler = async ( - event: EventBridgeEvent, - _context: Context -): Promise => { - const parsedEvent = EventBridgeSchema.parse(event); - logger.info('Parsed event', parsedEvent); - - const orders: Order = EventBridgeEnvelope.parse(event, orderSchema); - logger.info('Parsed orders', orders); -}; -``` - -### Safe parsing - -When parsing data, you can use the `safeParse` method to avoid throwing errors and handle them manually: - -```typescript -import type { Context } from 'aws-lambda'; -import { parser } from '@aws-lambda-powertools/parser/middleware'; -import { z } from 'zod'; -import middy from '@middy/core'; -import type { - ParsedResult, - EventBridgeEvent, -} from '@aws-lambda-powertools/parser/types'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -const orderSchema = z.object({ - id: z.number().positive(), - description: z.string(), - items: z.array( - z.object({ - id: z.number().positive(), - quantity: z.number(), - description: z.string(), - }) - ), - optionalField: z.string().optional(), -}); - -type Order = z.infer; - -const lambdaHandler = async ( - event: ParsedResult, - _context: Context -): Promise => { - if (event.success) { - // (2)! - for (const item of event.data.items) { - logger.info('Processing item', { item }); - } - } else { - logger.error('Error parsing event', { event: event.error }); - logger.error('Original event', { event: event.originalEvent }); - } -}; - -export const handler = middy(lambdaHandler).use( - parser({ schema: orderSchema, safeParse: true }) -); -``` - -See the [safe parsing](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser#safe-parsing) section in the documentation for more details. - -### Built-in schemas and envelopes - -The utility provides a set of built-in schemas and envelopes to parse popular AWS event sources payloads, for example: - -```typescript -import type { Context } from 'aws-lambda'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { parser } from '@aws-lambda-powertools/parser'; -import { z } from 'zod'; -import { EventBridgeEnvelope } from '@aws-lambda-powertools/parser/envelopes'; -import { Logger } from '@aws-lambda-powertools/logger'; - -const logger = new Logger(); - -const orderSchema = z.object({ - id: z.number().positive(), - description: z.string(), - items: z.array( - z.object({ - id: z.number().positive(), - quantity: z.number(), - description: z.string(), - }) - ), - optionalField: z.string().optional(), -}); - -type Order = z.infer; - -class Lambda implements LambdaInterface { - @parser({ schema: orderSchema, envelope: EventBridgeEnvelope }) - public async handler(event: Order, _context: Context): Promise { - // event is now typed as Order - for (const item of event.items) { - logger.info('Processing item', item); // (2)! - } - } -} - -const myFunction = new Lambda(); -export const handler = myFunction.handler.bind(myFunction); -``` - -Check the utility documentation for a complete list of built-in [schemas](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser/#built-in-schemas) and [envelopes](https://docs.aws.amazon.com/powertools/typescript/latest/features/parser/#built-in-envelopes). - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/tracer/API_README.md b/packages/tracer/API_README.md deleted file mode 100644 index df3ba7052e..0000000000 --- a/packages/tracer/API_README.md +++ /dev/null @@ -1,130 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). - -You can use the library in both TypeScript and JavaScript code bases. - -- [Intro](#intro) -- [Usage](#usage) - - [Basic usage](#basic-usage) - - [Capture AWS SDK clients](#capture-aws-sdk-clients) - - [Add metadata and annotations](#add-metadata-and-annotations) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [License](#license) - -## Intro - -The Tracer utility is an opinionated thin wrapper for [AWS X-Ray SDK for Node.js](https://github.com/aws/aws-xray-sdk-node), to automatically capture cold starts, trace HTTP(S) clients including `fetch` and generate segments and add metadata or annotations to traces. - -## Usage - -To get started, install the library by running: - -```sh -npm i @aws-lambda-powertools/tracer -``` - -### Basic usage - -Add `Tracer` to your Lambda handler as decorator: - -```ts -import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -import { Tracer } from '@aws-lambda-powertools/tracer'; - -const tracer = new Tracer({ serviceName: 'serverlessAirline' }); - -class Lambda implements LambdaInterface { - // Decorate your handler class method - @tracer.captureLambdaHandler() - public async handler(_event: unknown, _context: unknown): Promise { - tracer.getSegment(); - } -} - -const handlerClass = new Lambda(); -export const handler = handlerClass.handler.bind(handlerClass); -``` - -or using middy: - -```ts -import { Tracer } from '@aws-lambda-powertools/tracer'; -import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; -import middy from '@middy/core'; - -const tracer = new Tracer({ serviceName: 'serverlessAirline' }); - -const lambdaHandler = async ( - _event: unknown, - _context: unknown -): Promise => { - tracer.putAnnotation('successfulBooking', true); -}; - -// Wrap the handler with middy -export const handler = middy(lambdaHandler) - // Use the middleware by passing the Tracer instance as a parameter - .use(captureLambdaHandler(tracer)); -``` - -### Capture AWS SDK clients - -To capture AWS SDK clients, you can use the `captureAWSv3Client` method: - -```ts -import { Tracer } from '@aws-lambda-powertools/tracer'; -import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; - -const tracer = new Tracer({ serviceName: 'serverlessAirline' }); -// Instrument the AWS SDK client -const client = tracer.captureAWSv3Client(new SecretsManagerClient({})); - -export default client; -``` - -### Add metadata and annotations - -You can add metadata and annotations to trace: - -```ts - -import { Tracer } from '@aws-lambda-powertools/tracer'; - -const tracer = new Tracer({ serviceName: 'serverlessAirline' }); - -export const handler = async ( - _event: unknown, - _context: unknown -): Promise => { - const handlerSegment = tracer.getSegment()?.addNewSubsegment('### handler'); - handlerSegment && tracer.setSegment(handlerSegment); - - tracer.putMetadata('paymentResponse', { - foo: 'bar', - }); - tracer.putAnnotation('successfulBooking', true); - - handlerSegment?.close(); - handlerSegment && tracer.setSegment(handlerSegment?.parent); -}; -``` - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/packages/validation/API_README.md b/packages/validation/API_README.md deleted file mode 100644 index 91671ad15b..0000000000 --- a/packages/validation/API_README.md +++ /dev/null @@ -1,242 +0,0 @@ -# Powertools for AWS Lambda (TypeScript) - Validation Utility - -This utility provides JSON Schema validation for events and responses, including JMESPath support to unwrap events before validation. - -Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.aws.amazon.com/powertools/typescript/latest/#features). You can use the library in both TypeScript and JavaScript code bases. - -To get started, install the package by running: - -```sh -npm i @aws-lambda-powertools/validation -``` - -## Features - -You can validate inbound and outbound payloads using the `@validator` class method decorator or `validator` Middy.js middleware. - -You can also use the standalone `validate` function, if you want more control over the validation process such as handling a validation error. - -### Validator decorator - -The `@validator` decorator is a TypeScript class method decorator that you can use to validate both the incoming event and the response payload. - -If the validation fails, we will throw a `SchemaValidationError`. - -```typescript -import { validator } from '@aws-lambda-powertools/validation/decorator'; -import type { Context } from 'aws-lambda'; - -const inboundSchema = { - type: 'object', - properties: { - value: { type: 'number' }, - }, - required: ['value'], - additionalProperties: false, -}; - -const outboundSchema = { - type: 'object', - properties: { - result: { type: 'number' }, - }, - required: ['result'], - additionalProperties: false, -}; - -class Lambda { - @validator({ - inboundSchema, - outboundSchema, - }) - async handler(event: { value: number }, _context: Context) { - // Your handler logic here - return { result: event.value * 2 }; - } -} - -const lambda = new Lambda(); -export const handler = lambda.handler.bind(lambda); -``` - -It's not mandatory to validate both the inbound and outbound payloads. You can either use one, the other, or both. - -### Validator middleware - -If you are using Middy.js, you can instead use the `validator` middleware to validate the incoming event and response payload. - -```typescript -import { validator } from '@aws-lambda-powertools/validation/middleware'; -import middy from '@middy/core'; - -const inboundSchema = { - type: 'object', - properties: { - foo: { type: 'string' }, - }, - required: ['foo'], - additionalProperties: false, -}; - -const outboundSchema = { - type: 'object', - properties: { - bar: { type: 'number' }, - }, - required: ['bar'], - additionalProperties: false, -}; - -export const handler = middy() - .use(validation({ inboundSchema, outboundSchema })) - .handler(async (event) => { - // Your handler logic here - return { bar: 42 }; - }); -``` - -Like the `@validator` decorator, you can choose to validate only the inbound or outbound payload. - -### Standalone validate function - -The `validate` function gives you more control over the validation process, and is typically used within the Lambda handler, or any other function that needs to validate data. - -When using the standalone function, you can gracefully handle schema validation errors by catching `SchemaValidationError` errors. - -```typescript -import { validate } from '@aws-lambda-powertools/validation'; -import { SchemaValidationError } from '@aws-lambda-powertools/validation/errors'; - -const schema = { - type: 'object', - properties: { - name: { type: 'string' }, - age: { type: 'number' }, - }, - required: ['name', 'age'], - additionalProperties: false, -} as const; - -const payload = { name: 'John', age: 30 }; - -export const handler = async (event: unknown) => { - try { - const validatedData = validate({ - payload, - schema, - }); - - // Your handler logic here - } catch (error) { - if (error instanceof SchemaValidationError) { - // Handle the validation error - return { - statusCode: 400, - body: JSON.stringify({ message: error.message }), - }; - } - // Handle other errors - throw error; - } -} -``` - -### JMESPath support - -In some cases you might want to validate only a portion of the event payload - this is what the `envelope` option is for. - -You can use JMESPath expressions to specify the path to the property you want to validate. The validator will unwrap the event before validating it. - -```typescript -import { validate } from '@aws-lambda-powertools/validation'; - -const schema = { - type: 'object', - properties: { - user: { type: 'string' }, - }, - required: ['user'], - additionalProperties: false, -} as const; - -const payload = { - data: { - user: 'Alice', - }, -}; - -const validatedData = validate({ - payload, - schema, - envelope: 'data', -}); -``` - -### Extending the validator - -Since the validator is built on top of [Ajv](https://ajv.js.org/), you can extend it with custom formats and external schemas, as well as bringing your own `ajv` instance. - -The example below shows how to pass additional options to the `validate` function, but you can also pass them to the `@validator` decorator and `validator` middleware. - -```typescript -import { validate } from '@aws-lambda-powertools/validation'; - -const formats = { - ageRange: (value: number) => return value >= 0 && value <= 120, -}; - -const definitionSchema = { - $id: 'https://example.com/schemas/definitions.json', - definitions: { - user: { - type: 'object', - properties: { - name: { type: 'string' }, - age: { type: 'number', format: 'ageRange' }, - }, - required: ['name', 'age'], - additionalProperties: false, - } - } -} as const; - -const schema = { - $id: 'https://example.com/schemas/user.json', - type: 'object', - properties: { - user: { $ref: 'definitions.json#/definitions/user' }, - }, - required: ['user'], - additionalProperties: false, -} as const; - -const payload = { - user: { - name: 'Alice', - age: 25, - }, -}; - -const validatedData = validate({ - payload, - schema, - externalRefs: [definitionSchema], - formats, -}); -``` - -For more information on how to use the `validate` function, please refer to the [documentation](https://docs.aws.amazon.com/powertools/typescript/latest/features/validation). - -## Contribute - -If you are interested in contributing to this project, please refer to our [Contributing Guidelines](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/CONTRIBUTING.md). - -## Roadmap - -The roadmap of Powertools for AWS Lambda (TypeScript) is driven by customers’ demand. -Help us prioritize upcoming functionalities or utilities by [upvoting existing RFCs and feature requests](https://github.com/aws-powertools/powertools-lambda-typescript/issues), or [creating new ones](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new/choose), in this GitHub repository. - -## Connect - -- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -- **Email**: diff --git a/typedoc.json b/typedoc.json index 57017cc3fb..8ed13d1bb8 100644 --- a/typedoc.json +++ b/typedoc.json @@ -2,7 +2,7 @@ "entryPoints": ["packages/*"], "entryPointStrategy": "packages", "name": "Powertools for AWS Lambda (Typescript) API Reference", - "readme": "README.md", + "readme": "./API_README.md", "out": "api", "exclude": [ "**/node_modules/**",