Skip to content

Feature request: Middleware Chain API for Powertools Utilities#2202

@phipag

Description

@phipag

Use case

Currently, Powertools for AWS Lambda (Java) provides individual utilities like Logging, Tracing, Metrics, and other features through AspectJ annotations and static methods. While effective, this approach lacks a unified and modern functional interface for applying multiple Powertools utilities to Lambda handlers without relying on AspectJ annotations.

Developers need a solution that offers:

  • A unified functional API to compose multiple Powertools features (logging, tracing, metrics, validation, etc.)
  • Type-safe middleware composition that works across all Lambda handler types (RequestHandler, RequestStreamHandler, etc.)
  • Clean separation between business logic and cross-cutting concerns without AspectJ dependencies
  • Flexible composition allowing different orders and combinations of middlewares
  • Modern Java functional programming patterns that feel idiomatic to Java developers

This would provide an alternative to the current AspectJ annotation-based approach while maintaining the same powerful utility features.

Solution/User Experience

Introduce a middleware chain API that allows functional composition of Powertools features. The design below suggests an implementation of the Chain of Responsibility software design pattern:

@FunctionalInterfacepublicinterfaceMiddleware<T, R>{Rapply(Tinput, Contextcontext, BiFunction<T, Context, R> next)} publicclassMiddlewareChain<T, R>{publicMiddlewareChain<T, R> use(Middleware<T, R> middleware){/* ... */ } publicRexecute(Tinput, Contextcontext, BiFunction<T, Context, R> handler){/* ... */ } } publicclassPowertoolsMiddlewares{publicstatic <T, R> Middleware<T, R> logging(){/* ... */ } publicstatic <T, R> Middleware<T, R> tracing(StringserviceName){/* ... */ } publicstatic <T, R> Middleware<T, R> metrics(Stringnamespace){/* ... */ } }

Usage example:

publicclassOrderHandlerimplementsRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent>{privatefinalMiddlewareChain<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> chain = newMiddlewareChain<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent>() .use(PowertoolsMiddlewares.logging()) .use(PowertoolsMiddlewares.tracing("OrderService")) .use(PowertoolsMiddlewares.metrics("Orders")); @OverridepublicAPIGatewayProxyResponseEventhandleRequest(APIGatewayProxyRequestEventinput, Contextcontext){returnchain.execute(input, context, this::processOrder)} privateAPIGatewayProxyResponseEventprocessOrder(APIGatewayProxyRequestEventrequest, Contextcontext){// Business logic herereturnAPIGatewayProxyResponseEvent.builder() .withStatusCode(200) .withBody("{\"message\": \"Order processed\"}") .build()} }

Benefits:

  • Type-safe: Works with any Lambda handler type through generics
  • Composable: Middlewares can be combined in any order
  • Functional: Leverages modern Java functional programming patterns
  • Consistent: Same API works for RequestHandler, RequestStreamHandler, etc.
  • Flexible: Easy to add custom middlewares or modify the chain

Alternative solutions

Decorator pattern: Wrap handlers with individual decorators

  • Pros: Object-oriented approach, familiar pattern
  • Cons: More verbose, harder to compose multiple decorators, requires creating wrapper classes for each feature

Aspect-Oriented Programming (AOP): Frameworks like Spring AOP are too heavy for Lambda environments and introduce additional dependencies that may not be suitable for serverless architectures.

Acknowledgment


Disclaimer: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected.

Future readers: Please react with 👍 and your use case to help us understand customer demand.

Sub-issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Ideas

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions