Skip to content

RFC: Provide a middleware factory method for event_handler components #1566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 of 2 tasks
walmsles opened this issue Oct 4, 2022 · 4 comments
Closed
1 of 2 tasks

Comments

@walmsles
Copy link
Contributor

walmsles commented Oct 4, 2022

Is this related to an existing feature request or issue?

#1547

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

@walmsles walmsles added RFC triage Pending triage from maintainers labels Oct 4, 2022
@walmsles
Copy link
Contributor Author

walmsles commented Oct 9, 2022

Oh - can probably close this in favour of #953 😁, did not realise it was already in dev.

@heitorlessa
Copy link
Contributor

Closing in favour of #953 as I'm gonna aggregate all distinct conversations - another similar one came up #1955

@heitorlessa heitorlessa closed this as not planned Won't fix, can't repro, duplicate, stale Mar 1, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Mar 1, 2023

⚠️COMMENT VISIBILITY WARNING⚠️

This issue is now closed. Please be mindful that future comments are hard for our team to see.

If you need more assistance, please either tag a team member or open a new issue that references this one.

If you wish to keep having a conversation with other community members under this issue feel free to do so.

1 similar comment
@github-actions
Copy link
Contributor

github-actions bot commented Mar 1, 2023

⚠️COMMENT VISIBILITY WARNING⚠️

This issue is now closed. Please be mindful that future comments are hard for our team to see.

If you need more assistance, please either tag a team member or open a new issue that references this one.

If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests

3 participants