Description
Is this related to an existing feature request or issue?
Which AWS Lambda Powertools utility does this relate to?
Event Handler - REST API
Summary
AWS Lambda Powertools provides the Middleware factory which is great for creating middleware for Lambda Event handlers which has been the focus of middleware since inception of the python powertools library. With the API style Event Handlers based from BaseRouter we have provided a way for customers to create larger, more complex monolithic style APIs. This can cause code to become more complex and harder to maintain due to cross-cutting concerns that have an effect on multiple routes.
Use case
Enable middleware to run before/after a route to enable custom data to be injected to the route in a named parameter. being wrapped up as middleware will enable re-use simply and easily for other routes.
Today this is not possible "cleanly" due to the Lambda Event and COntext being locked away within the Router class and not exposed to the route functions at all (which makes complete sense).
To add-in Middleware would require something like the following today to enable passing of custom "user_data" into the route function handler. In the below code the middleware cannot be self contained in nature and would need to access the global "app" instance to access the original Lambda Event or Context which is not ideal for middleware implementations
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools import Logger
import functools
app = APIGatewayRestResolver()
logger = Logger()
def route_decorator(route_func):
"""Define route decorator middleware function to add my user data"""
@functools.wraps(route_func)
def wrapper(*args, **kwargs):
kwargs["user_data"] = {"param1":"value1"}
logger.info("calling route_decorator")
return route_func(*args, **kwargs)
return wrapper
@app.get("/hello")
@route_decorator
def hello_world(user_data=None):
return {"message": "Hello World", "user_data": user_data}
@logger.inject_lambda_context(log_event=True)
def lambda_handler(event, context):
return app.resolve(event, context)
Proposal
Provide an event handler middleware factory implementation to simplify setup of route middleware and provide access to key data within the middleware parameters: event, context, router to reduce boilerplate and provide a simpler mechanism for code re-use for cross-cutting concerns.
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
from aws_lambda_powertools.middleware_factory import router_lambda_decorator
from aws_lambda_powertools import Logger
app = APIGatewayRestResolver()
logger = Logger()
@router_lambda_decorator
def route_decorator(handler, router):
"""Define route decorator middleware function to add my user data"""
user_data = call_some_func(router.current_event)
response = handler(user_data)
return call_some_response_wrapper(response)
@app.get("/hello")
@route_decorator
def hello_world(user_data=None):
return {"message": "Hello World", "user_data": user_data}
@app.get("/foo")
@route_decorator
def get_foo(user_data=None):
return {"message": "More Foo, Less Bar", "user_data": user_data}
@logger.inject_lambda_context(log_event=True)
def lambda_handler(event, context):
return app.resolve(event, context)
Out of scope
...
Potential challenges
The main challenge with Middleware here is the fact that the routing middleware is interacting with the actual Resolver class and adding data to it for routing purposes which is a little different than the lambda_handler_decorator factory method so care is required in putting this solution together so that the call chaining makes sense and middleware function itself can be constructed in the same way all other python middleware functions are built.
Now that I've written this I feel it is less of a concern given the "app.resolve()" simply ends up calling the function and likely that is where the change for this feature actually is.
Dependencies and Integrations
No response
Alternative solutions
All other alternative solutions end up being a little "hacky" in nature. I thought the same effect could be achieved using lambda_handler_decorator instead but the trouble is that fact the "app.resolve()" router function does not enable passing of data into it causing any central lambda handler mechanism requiring storing of globals or manipulating the actual event/context objects themselves which is less than ideal.
Middleware with correctly injected data dependencies is always nicer allowing self contained processing for the route handlers so this feature will no longer require global variables to be setup to pollute the lambda execution environment and potentially cause side effects between function invocations which is possible.
Acknowledgment
- This feature request meets Lambda Powertools Tenets
- Should this be considered in other Lambda Powertools languages? i.e. Java, TypeScript
Metadata
Metadata
Assignees
Type
Projects
Status