Skip to content

Releases: aws-powertools/powertools-lambda-typescript

v2.33.0

14 Apr 12:58
5765cc4

Choose a tag to compare

Summary

In this release, the HTTP event handler gains a new metrics middleware that lets you emit per-request latency, fault, and error metrics with a single line of configuration. The middleware automatically uses the matched route as a dimension and attaches request metadata like HTTP method, path, status code, and API Gateway request IDs.

We've also improved the parser package by exporting InferOutput from the public types entry point, resolving a TypeScript declaration emit error (TS2883) that affected consumers using safeParse mode — particularly those upgrading to TypeScript 6.

⭐ Congratulations to @yashar-new10 and @faberchri for their first PR merged in the project 🎉

Metrics Middleware

You can now use the Metrics utility with the HTTP event handler to automatically emit CloudWatch metrics for every request. The middleware:

  • Adds the matched route as a metric dimension (e.g., GET /users/:id)
  • Emits latency (Milliseconds), fault (Count), and error (Count) metrics
  • Attaches request metadata including httpMethod, path, statusCode, userAgent, and ipAddress
  • Adds API Gateway-specific metadata (apiGwRequestId, apiGwApiId) when available
  • Uses NOT_FOUND as the route dimension when no route matches, preventing dimension explosion
import { Router } from '@aws-lambda-powertools/event-handler/http';
import { metrics as metricsMiddleware } from '@aws-lambda-powertools/event-handler/http/middleware/metrics';
import { Metrics } from '@aws-lambda-powertools/metrics;
import type { Context } from 'aws-lambda';

const metrics = new Metrics({ namespace: 'my-app', serviceName: 'my-service' });
const app = new Router();

app.use(metricsMiddleware(metrics));

app.get('/users/:id', async ({ params }) => {
  return { id: params.id, name: 'Jane' };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Changes

  • improv(parser): export InferOutput from public types entry point (#5175) by @yashar-new10
  • chore(deps): add esbuild as explicit devDependency (#5173) by @sdangol
  • chore: temporarily remove Bahrain (me-south-1) region from layer publishing (#5169) by @svozza
  • docs(commons): fix isStrictEqual and areArraysEqual JSDoc array comparison description (#5165) by @Zelys-DFKH
  • docs: fix dependencies & broken references (#5156) by @dreamorosi
  • docs: fix markdown table format (#5153) by @dothomson
  • style: remove useAwait linting rule (#5146) by @dreamorosi
  • docs(event-handler): update custom middleware example to use Store API (#5145) by @svozza
  • fix(commons): don't overwrite existing value with undefined in deepMerge (#5141) by @faberchri
  • fix(event-handler): http response body validation typings (#5125) by @nateiler
  • feat(event-handler): add metrics middleware for HTTP routes (#5086) by @svozza
  • fix: update middy types to be aligned with 7.1.2 update (#5100) by @willfarrell
  • fix(commons): rename AvailabilityZoneId to AvailabilityZoneID in docs and tests (#5118) by @svozza

🔧 Maintenance

This release was made possible by the following contributors:

@Zelys-DFKH, @dependabot[bot], @dothomson, @dreamorosi, @faberchri, @github-actions[bot], @nateiler, @sdangol, @svozza, @willfarrell, @yashar-new10, dependabot[bot] and github-actions[bot]

v2.32.0

20 Mar 16:13
0abf2f0

Choose a tag to compare

Summary

In this release, we are pleased to announce a new utility for interacting with the Lambda Metadata Service in the commons package, allowing you to easily retrieve information about the Lambda function, such as the Availability Zone ID.

In the HTTP event handler, we have delivered two much requested features: a type-safe Store API, letting you share state between middleware and route handlers with full type safety, and request and response validation, so you can enforce data contracts at your API boundary, eliminating manual parsing and type assertion code.

We've also fixed a bug where the Kafka consumer would throw when processing tombstone events with undefined values.

⭐ Congratulations to @haslers for their first PR merged in the project 🎉

Lambda Metadata Service

A new getMetadata() utility in the commons package fetches metadata from the AWS Lambda Metadata endpoint. The utility automatically returns an empty object outside of Lambda, so your code works seamlessly in local development and testing.

import { getMetadata } from '@aws-lambda-powertools/commons/utils/metadata';
import { Logger } from '@aws-lambda-powertools/logger';

const logger = new Logger({ serviceName: 'serverlessAirline' });
const metadata = await getMetadata();

export const handler = async () => {
  const { AvailabilityZoneID: azId } = metadata;
  logger.appendKeys({ azId });
};

Type-safe Store API

The HTTP event handler now includes a Store API that provides type-safe, scoped state management for route handlers with two built-in storage scopes:

  • Request store — per-invocation state (set in middleware, read in handlers). Cleared automatically between requests.
  • Shared store — router-scoped state (set at cold start, read everywhere). Persists across invocations.
import { Router } from '@aws-lambda-powertools/event-handler/http';
import type { Context } from 'aws-lambda';

type AppEnv = {
  store: {
    request: { userId: string; isAdmin: boolean };
    shared: { db: { query: (sql: string) => Promise<unknown> } };
  };
};

const app = new Router<AppEnv>();

app.shared.set('db', createDbClient());

app.use(async ({ reqCtx, next }) => {
  const auth = reqCtx.req.headers.get('Authorization') ?? '';
  const { sub, isAdmin } = jwt.verify(auth.replace('Bearer ', ''), 'secret');
  reqCtx.set('userId', sub);
  reqCtx.set('isAdmin', isAdmin);
  await next();
});

app.get('/profile', async (reqCtx) => {
  const userId = reqCtx.get('userId');   // typed as string
  const db = reqCtx.shared.get('db');    // typed as { query: ... }
  if (!userId || !db) return { error: 'not ready' };
  return { userId };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Validation Middleware

The new validation feature validates validates request and response data against Standard Schema v1 schemas — a vendor-neutral interface supported by Zod, Valibot, ArkType, and other popular libraries.

You can validate any combination of request body, headers, path parameters, and query parameters. Request validation errors return a structured 422 Unprocessable Entity response, and response validation errors return 500 Internal Server Error. TypeScript infers the handler's context type from your schema config — only validated fields are accessible, catching mismatches at compile time.

import { Router } from '@aws-lambda-powertools/event-handler/http';
import type { Context } from 'aws-lambda';
import { z } from 'zod';

const app = new Router();

const createTodoSchema = z.object({ title: z.string() });

const todoResponseSchema = z.object({
  id: z.string(),
  title: z.string(),
  completed: z.boolean(),
});

app.post(
  '/todos',
  (reqCtx) => {
    const { title } = reqCtx.valid.req.body; // fully typed
    return { id: '123', title, completed: false };
  },
  {
    validation: {
      req: { body: createTodoSchema },
      res: { body: todoResponseSchema }, // runtime check on the response
    },
  }
);

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Changes

  • fix(commons): rename AvailabilityZoneId to AvailabilityZoneID in docs and tests (#5118) by @svozza
  • chore(ci): temporarily disable me-central-1 deployments (#5111) by @svozza
  • feat(commons): add Lambda Metadata Service support (#5109) by @svozza
  • revert: feat(commons): add Lambda Metadata Service support (#5106) (#5107) by @svozza
  • feat(commons): add Lambda Metadata Service support (#5106) by @svozza
  • feat(event-handler): add type-safe Store API for request and shared state (#5081) by @svozza
  • chore(deps): regenerate package-lock.json to clear stale Dependabot alerts (#5075) by @dreamorosi
  • fix(event-handler): add overloads to route() for typed validation context (#5052) by @svozza
  • chore: run linting on packages, run npm audit fix manually, address SonarCloud (#5046) by @dreamorosi
  • fix(event-handler): default error handler returns a web Response correctly (#5024) by @nateiler
  • fix(kafka): handle tombstone events without value (#5017) by @haslers
  • ci: use ts extension in layer CDK stack imports (#5010) by @svozza
  • ci: fix layer import in CDK stack (#5009) by @dreamorosi

📜 Documentation updates

  • feat(event-handler): add validation support for REST router (#4736) by @sdangol

🔧 Maintenance

Read more

v2.31.0

12 Feb 13:13
54d1fa3

Choose a tag to compare

Summary

In this release we are pleased to announce Tracer middleware for the HTTP event handler, which allows users to enable distributed tracing for their HTTP routes with minimal boilerplate code.

In addition, the metric utility now supports a fluent interface, allowing you to chain multiple methods in a single statement.

We have also fixed a bug in the HTTP event handler that caused parameterized headers to be handled incorrectly.

⭐ Special thanks to @nateiler and @dothomson for their first PR merged in the project, and to @arnabrahman! for another great contribution 🎉

Tracer Middleware

You can now use the Tracer utility with the HTTP event handler to gain observability over your routes. The middleware:

  • Creates a subsegment for each HTTP route with the format METHOD /path (e.g., GET /users)
  • Adds ColdStart and Service annotations
  • Optionally captures JSON response bodies as metadata
  • Captures errors as metadata when exceptions occur
import { Router } from '@aws-lambda-powertools/event-handler/http';
import { tracer as tracerMiddleware } from '@aws-lambda-powertools/event-handler/http/middleware/tracer';
import { Tracer } from '@aws-lambda-powertools/tracer';
import type { Context } from 'aws-lambda';

const tracer = new Tracer({ serviceName: 'my-api' });
const app = new Router();

app.get(
  '/users/cards',
  [tracerMiddleware(tracer, { captureResponse: false })],
  ({ params }) => {
    return { id: params.id, secret: 'sensitive-data' };
  }
);

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Metrics Fluent Interface

All mutation methods (with the exception of clear*) now return the metric instance that was mutated, allowing you to chain multiple metrics operations in a single statement.

import { Metrics} from '@aws-lambda-powertools/metrics';

const metrics = new Metrics();

metrics
    .addDimension('environment', 'prod')
    .addDimension('commit', '1234')
    .addMetric('test1', MetricUnit.Count, 1);
    .addMetric('test2', MetricUnit.Count, 1);

Changes

📜 Documentation updates

🔧 Maintenance

Read more

v2.30.2

12 Jan 12:31
48a557d

Choose a tag to compare

Summary

In this release we have added Middy v7 middleware compatibility which enables developers using Middy to use Durable functions, Tenant isolation mode, and multi-concurrency on Lambda Managed Instances.

We have also now added a new tenantId property by default for tenant identification in all log entries, making it easier to filter and trace logs across different tenants in your Lambda functions.

⭐ Special thanks to @coderbyheart for their first PR merged in the project 🎉

Changes

📜 Documentation updates

  • chore(deps): bump urllib3 from 2.6.0 to 2.6.3 in /docs (#4927) by @dependabot[bot]
  • improv(docs): added a note for customers migrating from BatchProcessorSync to BatchProcessor (#4926) by @sdangol
  • improv(docs): updated the Maintainer's Playbook with the updated CI process (#4922) by @sdangol

🔧 Maintenance

  • chore(batch): remove invoke store global namespace workaround (#4932) by @svozza
  • chore(deps): bump urllib3 from 2.6.0 to 2.6.3 in /docs (#4927) by @dependabot[bot]
  • feat: remove baseUrl from TypeScript configurations for future compiler compatibility (#4925) by @dreamorosi
  • improv(docs): updated the Maintainer's Playbook with the updated CI process (#4922) by @sdangol

This release was made possible by the following contributors:

@coderbyheart, @dependabot[bot], @dreamorosi, @github-actions[bot], @kawaaaas, @sdangol, @svozza, dependabot[bot] and github-actions[bot]

v2.30.1

06 Jan 16:31
1ec1c03

Choose a tag to compare

Summary

In this release we have removed lodash.merge , allowing users to simplify their bundling process as special handling of this CommonJS only dependency is no longer required.

⭐ Special thanks to @jaimellamasi for their work on the lodash migration! 🎉

Changes

  • improv(ci): Added environment suffix to prevent artefact name conflict (#4921) by @sdangol
  • fix(logger): improve e2e test resilience by grouping logs by level (#4909) by @dreamorosi
  • chore(ci): prevent parameter injection in make-version workflow (#4900) by @dreamorosi
  • chore(ci): harden workflow permissions for security compliance (#4898) by @dreamorosi
  • improv(ci): fixed the Make Release workflow (#4867) by @sdangol
  • improv(ci): automated the Gamma/Prod deployment to China/GovCloud regions (#4864) by @sdangol

📜 Documentation updates

🔧 Maintenance

This release was made possible by the following contributors:

@dependabot[bot], @dreamorosi, @github-actions[bot], @jaimellamasi, @sdangol, dependabot[bot] and github-actions[bot]

v2.30.0

11 Dec 17:23
dd5395b

Choose a tag to compare

Summary

We're excited to announce AWS Lambda durable function support in Powertools for AWS Lambda TypeScript. This allows you to leverage AWS Lambda's new durable execution capabilities while taking the advantage of features such as maintaining idempotency guarantees, making it easier to build reliable serverless workflows that span multiple invocations.

AWS Lambda durable functions allow you to simplify building multi-step applications and AI workflows. These functions automatically checkpoint progress, suspend execution for up to one year during long-running tasks, and recover from failures without the need to manage additional infrastructure.

Note

Lambda function handlers using MiddyJS or Class method decorators aren’t compatible with Durable Function handlers at this time.
Tracking issues: MiddyJS middyjs/middy#1531
Class Decorator #4832

This release also includes some bug fixes and a documentation improvement with the help of the community.

⭐ Congratulations to @jinxiao and @dwandro for their first PR merged in the project 🎉

Changes

  • fix(logger): merged temp keys with same keys when appending to the logger (#4840) by @sdangol
  • fix(idempotency): Update durableExecutionMode string (#4837) by @ConnorKirk
  • feat(idempotency): Allow durable function to replay (#4834) by @ConnorKirk
  • fix(event-handler): threshold limit for compression not respected when content-length not set (#4827) by @svozza
  • feat(parser): fix DynamoDBStreamChangeRecordSchema.dynamodb type (#4817) by @benthorner

📜 Documentation updates

🔧 Maintenance

This release was made possible by the following contributors:

@ConnorKirk, @benthorner, @dependabot[bot], @dwandro, @github-actions[bot], @jinxiao, @sdangol, @svozza, dependabot[bot] and github-actions[bot]

v2.29.0

21 Nov 20:02
fa726e0

Choose a tag to compare

Summary

🎉 Powertools for AWS Lambda (Typescript) - Event Handler Utility is now Generally Available (GA)

Docs

We're excited to announce that the Event Handler utility is now production-ready! 🚀
Event Handler provides lightweight routing to reduce boilerplate for API Gateway REST/HTTP API, ALB and Lambda Function URLs.

⭐ Congratulations to @yoshi-taka, @iamgerg, @fidelisojeah, and @benthorner for their first PR merged in the project 🎉

Import path update

With Event Handler moving to GA, the import path has changed from the experimental namespace to a stable one.

// Before
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';

// Now
import { Router } from '@aws-lambda-powertools/event-handler/http';

Support for HTTP APIs, ALB, and Function URL

Event Handler now supports HTTP APIs (API Gateway v2), Application Load Balancers (ALB) and Lambda Function URL in addition to the existing REST API and support. This means you can use the same routing API across different AWS services, making it easier to build and migrate serverless applications regardless of your chosen architecture.

import { Router } from '@aws-lambda-powertools/event-handler/http';
import type {
  ALBEvent,
  APIGatewayProxyEvent,
  APIGatewayProxyEventV2,
  Context,
  LambdaFunctionURLEvent,
} from 'aws-lambda';

const app = new Router();
app.get('/hello', () => {
  return {
    message: 'Hello Event Handler!',
  };
});

// Works across different services without any changes 
export const restApiHandler = (event: APIGatewayProxyEvent, context: Context) =>
  app.resolve(event, context);

export const httpApiHandler = (
  event: APIGatewayProxyEventV2,
  context: Context
) => app.resolve(event, context);

export const albHandler = (event: ALBEvent, context: Context) =>
  app.resolve(event, context);

export const lambdaFunctionUrlHandler = (
  event: LambdaFunctionURLEvent,
  context: Context
) => app.resolve(event, context);

Response Streaming

You can now stream responses directly from your Lambda functions, enabling use cases like serving large files, real-time data feeds, or progressive rendering. You just need to wrap your Router with streamify and you’re good to go.

import {
  Router,
  streamify,
} from '@aws-lambda-powertools/event-handler/http';

const app = new Router();

app.get('/video-stream', async (reqCtx) => {
  reqCtx.res.headers.set('content-type', 'video/mp4');
  return createVideoStream();
});

app.get('/hello', () => {
  return { message: 'Hello World' };
});

export const handler = streamify(app);

Binary Response

Event Handler now also includes first-class support for binary responses, making it straightforward to serve images, PDFs, or any other binary content from your Lambda functions. The utility handles the necessary encoding and content-type headers automatically.

import { createReadStream } from 'node:fs';
import { Router } from '@aws-lambda-powertools/event-handler/http';
import type { Context } from 'aws-lambda';

const app = new Router();

app.get('/logo', async (reqCtx) => {
  reqCtx.res.headers.set('Content-Type', 'image/png');
  return createReadStream(`${process.env.LAMBDA_TASK_ROOT}/logo.png`);
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Changes

  • feat(parser): add type for values parsed by DynamoDBStreamRecord (#4793) by @benthorner
  • fix(event-handler): moved the response mutation logic to the composeMiddleware function (#4773) by @sdangol
  • feat(event-handler): add support for ALB (#4759) by @svozza
  • test(maintenance): Expose ResponseStream object to simplify testing in event handler (#4753) by @svozza
  • fix(event-handler): handle repeated queryString values (#4755) by @dreamorosi
  • test(maintenance): remove unnecessary esbuild banner from e2e tests (#4745) by @svozza
  • feat(event-handler): expose response streaming in public API (#4743) by @svozza
  • fix(logger): infinite loop on log buffer when item size is max bytes (#4741) by @fidelisojeah
  • fix(logger): not passing persistent keys to children (#4740) by @iamgerg
  • fix(event-handler): allow event handler response to return array (#4725) by @arnabrahman
  • feat(event-handler): add first-class support for binary responses (#4723) by @svozza
  • feat(event-handler): Add support for HTTP APIs (API Gateway v2) (#4714) by @svozza
  • chore(event-handler): unflag http handler from experimental (#4801) by @svozza
  • feat(batch): use async local storage for batch processing (#4700) by @svozza
  • feat(logger): use async local storage for logger (#4668) by @svozza
  • feat(metrics): use async local storage for metrics (#4663) (#4694) by @svozza
  • improv(commons): Make trace ID access more robust (#4693) by @svozza

📜 Documentation updates

🔧 Maintenance

Read more

v2.28.1

23 Oct 16:48
e2ce325

Choose a tag to compare

Summary

This patch release addresses an issue in the Commons package utility introduced in v2.28.0 that caused a runtime error.

This issue affects only those who have lambda code that depends directly or indirectly on the getXrayTraceDataFromEnv function in the Commons package and who are bundling that lambda with ESBuild to an ES module.

We recommend updating to the latest version to avoid the issue.

Changes

📜 Documentation updates

🔧 Maintenance

This release was made possible by the following contributors:

@dependabot[bot], @github-actions[bot], @sdangol, @svozza, dependabot[bot] and github-actions[bot]

v2.28.0

22 Oct 09:20
5ea8799

Choose a tag to compare

Summary

We are excited to announce that the REST API Event Handler now supports catch-all routes, allowing you to use regex patterns directly when defining route paths. We've also added the ability to split routers using includeRouter for both REST API and AppSync GraphQL Event Handlers.

We’ve also reverted the SQSRecordSchema change that caused failed parsing of records when md5OfMessageAttributes was null.

📜 Announcement: You can now find our documentation on the official AWS documentation domain at https://docs.aws.amazon.com/powertools/typescript/latest/

⭐ Congratulations @mdesousa, @thiagomeireless, @alex-karo for their first PR merged in the project 🎉

Catch-all route

You can now use regex patterns in your routes to handle arbitrary or deeply nested paths.

import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';

const app = new Router();

// Instead of defining every possible path
app.get('/files/:folder/:subfolder/:filename');

// Use regex to handle any depth
app.get(/\/files\/.*/, async (reqCtx) => {
  // This will match:
  // /files/docs/2025/report.pdf
  // /files/images/avatars/user1/profile.jpg
  // /files/any/number/of/nested/paths/file.txt
});

We recommend having explicit routes whenever possible. Catch-all routes should be used sparingly and the pattern should be documented.

Split Routers

You can now define routes in separate files and import them into a main router file, improving code organization and maintainability.

REST API Event Handler

// userRoutes.ts
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';

const userRouter = new Router();
userRouter.get('/', listUsers);
userRouter.post('/', createUser)
userRouter.get('/:id', getUser)

export { userRouter };
// orderRoutes.ts
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';

const orderRouter = new Router();
orderRouter.get('/orders', listOrders);
orderRouter.post('/orders', createOrder);
orderRouter.put('/orders/:id/status', updateStatus);

export { orderRouter };
// main.ts
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
import type { APIGatewayProxyEvent } from 'aws-lambda';
import type { Context } from 'aws-lambda';
import { userRouter } from './userRoutes.js';
import { orderRouter } from './orderRoutes.js';

const app = new Router();

// Split Routers
app.includeRouter(userRouter, { prefix: '/users' });
app.includeRouter(orderRouter, { prefix: '/orders' });

export const handler = async (event: APIGatewayProxyEvent, context: Context) =>
  app.resolve(event, context);

AppSync GraphQL Event Handler

// postRouter.ts
import { Router } from '@aws-lambda-powertools/event-handler/appsync-graphql';

const postRouter = new Router();

postRouter.onQuery('getPosts', getPosts);
postRouter.onMutation('createPost', createPost);

export { postRouter };
//userRouter.ts
import { Router } from '@aws-lambda-powertools/event-handler/appsync-graphql';

const userRouter = new Router();

userRouter.onQuery('getUsers', getUsers);

export { userRouter };
// main.ts
import { AppSyncGraphQLResolver } from '@aws-lambda-powertools/event-handler/appsync-graphql';
import type { Context } from 'aws-lambda';
import { postRouter } from './postRouter';
import { userRouter } from './userRouter';

const app = new AppSyncGraphQLResolver();

app.includeRouter([postRouter, userRouter]);

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Changes

  • improv(event-handler): ended response stream when body is null (#4651) by @sdangol
  • fix(idempotency): add null check for idempotencyHandler before calling handleMiddyOnError (#4643) by @mdesousa
  • fix(parser): updated the SQSRecordSchema to make the md5OfMessageAttributes nullable (#4632) by @sdangol
  • fix(event-handler): allow http handlers to return duplex streams (#4629) by @svozza
  • fix(logger): correct persistentLogAttributes warning behavior (#4627) by @alex-karo
  • feat(event-handler): added support for catch all route (#4582) by @sdangol
  • improv(event-handler): rename ServiceError class to HttpError (#4610) by @svozza
  • docs(parser): correct typos in example code for middleware (#4607) by @thiagomeireless
  • test: update domain in e2e tests (#4606) by @dreamorosi
  • ci: add --delete flag to docs publish command (#4584) by @dreamorosi
  • feat(event-handler): added includeRouter method to split routes (#4573) by @sdangol
  • chore: sanitize CI inputs via env var (#4528) by @dreamorosi
  • feat(event-handler): Add includeRouter support to AppSync GraphQL resolver (#4457) by @arnabrahman

📜 Documentation updates

🔧 Maintenance

Read more

v2.27.0

24 Sep 13:35
f38af1d

Choose a tag to compare

Summary

We're excited to announce an update to our Event Handler utility, featuring new middleware for CORS (Cross-Origin Resource Sharing) configuration and automatic response compression. We have also added support for Route Prefixes which allows you to avoid repeating a shared prefix in each route definition.

We've listened to your feedback and also made some slight changes to the type signature in the handlers to improve developer experience.

We have also made the error handlers more versatile by allowing a Response object or a JavaScript object to be returned from the error handler.

⭐ Congratulations @guillemcomerma for their first PR merged in the project, and thank you to @shrivarshapoojari and @dani-abib for their contributions 🎉

CORS Middleware

Docs

The CORS middleware ensures CORS headers are returned as part of the response when your functions match the path invoked and the Origin matches one of the allowed values.

import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
import { cors } from '@aws-lambda-powertools/event-handler/experimental-rest/middleware';
import type { Context } from 'aws-lambda';

const app = new Router();

app.use(
  cors({
    origin: 'https://example.com',
    maxAge: 300,
  })
);

app.get('/todos/:todoId', async ({ params: { todoId } }) => {
  const todo = await getTodoById(todoId);
  return { todo };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
  
/**
When invoked from a valid origin, this returns:
  
{
  "statusCode": 200,
  "headers": {
    "access-control-allow-credentials": "false",
    "access-control-allow-origin": "https://example.com",
    "content-type": "application/json"
  },
  "multiValueHeaders": {
    "access-control-allow-headers": [
      "Authorization",
      "Content-Type",
      "X-Amz-Date",
      "X-Api-Key",
      "X-Amz-Security-Token"
    ],
    "access-control-allow-methods": [
      "DELETE",
      "GET",
      "HEAD",
      "PATCH",
      "POST",
      "PUT"
    ]
  },
  "body": "{\"todoId\":\"123\",\"task\":\"Example task\",\"completed\":false}",
  "isBase64Encoded": false
}
**/

Compress Middleware

Docs

The compress middleware automatically compresses responses using gzip and base64 encodes them when the client indicates support via the Accept-Encoding header.

import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
import { compress } from '@aws-lambda-powertools/event-handler/experimental-rest/middleware';
import type { Context } from 'aws-lambda';

const app = new Router();

app.use(compress());

app.get('/todos/:todoId', async ({ params: { todoId } }) => {
  const todo = await getTodoById(todoId);
  return { todo };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);
  
/**
When invoked with the following request,
{
  "headers": {
    "Accept-Encoding": "gzip"
  },
  "resource": "/todos/1",
  "path": "/todos/1",
  "httpMethod": "GET"
}

it would return,

{
  "statusCode": 200,
  "multiValueHeaders": {
    "Content-Type": [
      "application/json"
    ],
    "Content-Encoding": [
      "gzip"
    ]
  },
  "body": "H4sIAAAAAAACE42STU4DMQyFrxJl3QXln96AMyAW7sSDLCVxiJ0Kqerd8TCCUOgii1EmP/783pOPXjmw+N3L0TfB+hz8brvxtC5KGtHvfMCIkzZx0HT5MPmNnziViIr2dIYoeNr8Q1x3xHsjcVadIbkZJoq2RXU8zzQROLseQ9505NzeCNQdMJNBE+UmY4zbzjAJhWtlZ57sB84BWtul+rteH2HPlVgWARwjqXkxpklK5gmEHAQqJBMtFsGVygcKmNVRjG0wxvuzGF2L0dpVUOKMC3bfJNjJgWMrCuZk7cUp02AiD72D6WKHHwUDKbiJs6AZ0VZXKOUx4uNvzdxT+E4mLcMA+6G8nzrLQkaxkNEVrFKW2VGbJCoCY7q2V3+tiv5kGThyxfTecDWbgGz/NfYXhL6ePgF9PnFdPgMAAA==",
  "isBase64Encoded": true
}
**/

Route Prefixes

Docs

When defining multiple routes related to a specific resource, you can use the prefix constructor parameter when creating a new Router instance, and we'll automatically strip it from the request path before matching routes. This will prevent you to avoid repeating the prefix in each route definition.

import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
import type { Context } from 'aws-lambda';

const app = new Router({ prefix: '/todos' });

// matches POST /todos
app.post('/', async ({ req: { headers } }) => {
  const todos = await getUserTodos(headers.get('Authorization'));
  return { todos };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Type Signature Changes

We’ve also made changes to some type signatures to reduce parameter complexity.

The path parameters in the route handlers have been moved to reqCtx

app.get('/todos/:todoId', async (reqCtx) => {
  return { id: reqCtx.params.todoId };
});

Previously middleware used positional arguments:

const middleware: Middleware = async (params, reqCtx, next) => {
  logger.info('Request processed');
  await next();
};

Now, we have changed it to be an object so customers can pick only the parameters that they want and also moved the params parameter to reqCtx

const middleware: Middleware = async ({ reqCtx: { params }, next }) => {
  logger.info('Request processed');
  await next();
};

We have also changed the request property to req in the RequestContext type to maintain consistency with the res property.

Improvements on Error Handling

Error handlers can now return a Response object or a JavaScript object that will be auto-serialized.

import {
  HttpStatusCodes,
  Router,
} from '@aws-lambda-powertools/event-handler/experimental-rest';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda/handler';

const logger = new Logger();
const app = new Router({ logger });

app.errorHandler(GetTodoError, async (error, reqCtx) => {
  logger.error('Unable to get todo', { error });

  return {
    statusCode: HttpStatusCodes.BAD_REQUEST,
    message: `Bad request: ${error.message} - ${reqCtx.req.headers.get('x-correlation-id')}`,
  };
});

app.get('/todos/:todoId', async ({ params: { todoId } }) => {
  const todo = await getTodoById(todoId); // May throw GetTodoError
  return { todo };
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

Changes

  • chore: sanitize CI inputs via env var (#4528) by @dreamorosi
  • improv(event-handler): rename HttpErrorCodes to HttpStatusCodes (#4543) by @dreamorosi
  • docs(idempotency): fix dataKeywordArgument reference and remove unused test code (#4537) by @guillemcomerma
  • improv(event-handler): made error handler responses versatile (#4536) by @sdangol
  • chore(ci): add registry-url to setup-node steps to fix Scorecard packaging check (#4539) by @shrivarshapoojari
  • improv(event-handler): changed path parameter in middleware and routehandler signature (#4532) by @sdangol
  • improv(event-handler): changed the Middleware and RequestContext signatures (#4530) by @sdangol
  • fix(event-handler): fixed CORS behaviour not aligned with CORS spec (#4512) by @sdangol
  • feat(event-handler): implemented route prefixes in HTTP event handler (#4523) by @sdangol
  • feat(event-handler): throw error when middleware does not await next() (#4511) by @svozza
  • chore: always append PTEnv to UA string (#4522) by @dreamorosi
  • fix(event-handler): run global middleware on all requests for REST API (#4507) by @svozza
  • fix(batch): fixed the build issue with Batch processor due to missing dependencies (#4498) by @sdangol
  • feat(event-handler): added compress middleware for the REST API event handler (#4495) by @sdangol
  • feat(event-handler): add CORS middleware support (#4477) by @dcabib

📜 Documentation updates

Read more