Skip to content

Feature request: Async support for ApiGatewayResolver #3934

@pkucmus

Description

@pkucmus

Use case

I would love to be able to use asynchronous Python with Powertools. I understand there is not as much need for it in a Lambda runtime as a Lambda (process) will handle only one event but I would still be able to use async-only libraries like encode/databases (with the likes of Neon) or simply reduce my execution time by utilizing asyncio.gather when doing concurrent waits for I/O operations.

Solution/User Experience

I imagine being able to use it similarly to what the GraphQL resolver does:

@app.get("/todos")
@tracer.capture_method
async def get_todos():
    todos: httpx.Response = httpx.get("https://jsonplaceholder.typicode.com/todos")
    todos.raise_for_status()
    return {"todos": todos.json()[:10]}


# You can continue to use other utilities just as before
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
@tracer.capture_lambda_handler
def lambda_handler(event: dict, context: LambdaContext) -> dict:
    result = app.resolve(event, context)
    return asyncio.run(result)

Alternative solutions

No response

Acknowledgment

  • This feature request meets Powertools for AWS Lambda (Python) Tenets
    Should this be considered in other Powertools for AWS Lambda languages? i.e. Java, TypeScript, and .NET

Activity

boring-cyborg

boring-cyborg commented on Mar 11, 2024

@boring-cyborg

Thanks for opening your first issue here! We'll come back to you as soon as we can.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

leandrodamascena

leandrodamascena commented on Mar 12, 2024

@leandrodamascena
Contributor

Hey @pkucmus! Wowww, we're diving into some pretty cool Feature Request here: making our EventHandler and all its resolvers work async and add more flexibility to enhance I/O operations. But it comes with some significant changes ahead.
One big switch-up is how we handle object serialization. It basically means we'll need to rethink how we call Python functions and make all (or most) of them async. Another challenge is that our resolvers are a kind of Middleware between Lambda execution and the map between specific routes (GET /todos, POST /hello) to execute functions. That adds a bit of extra spice to the mix of how to execute these functions with coroutines.
And when it comes to our OpenAPI validation, currently, it's all about sync calls. Shifting this to asynchronous operations is quite a significant work.

To be completely transparent, I'm unable to quantify the extent of work needed to implement this capability right now. Perhaps we should consider drafting an RFC to outline the process in detail. It might even be worth exploring the creation of entirely new resolvers that operate entirely async.

It's encouraging to see "thumbs up" from many people regarding this matter, and I believe we should seriously consider it. To ensure we stay on track, I'll add some labels such as "revisit in 3 months" and "need help" to keep this issue on our radar and facilitate brainstorming for a solution.

Thank you and please let us know if you have in mind any kind of implementation or solution for this.

added
help wantedCould use a second pair of eyes/hands
revisit-in-3-monthsRequires more customers feedback before making or revisiting a decision
and removed
triagePending triage from maintainers
on Mar 12, 2024
pkucmus

pkucmus commented on Mar 13, 2024

@pkucmus
Author

Thank you @leandrodamascena, what a nice approach for a random idea like that. I would be happy to put in some work into this. Maybe this would not be as replacement for the existing resolvers but something additional? If you would like I could draft something but I would need to understand how it's currently working, i.e. the use of the global state is confusing me a lot - I'm sure there's a reason for it but I don't know why we're storing and accessing the event in a global manner. Is there chance for some guidance or rather I should stay put not to generate more work for you than needed?

BaseRouter.current_event = self._to_proxy_event(event)
BaseRouter.lambda_context = context

leandrodamascena

leandrodamascena commented on Mar 18, 2024

@leandrodamascena
Contributor

Thank you @leandrodamascena, what a nice approach for a random idea like that. I would be happy to put in some work into this. Maybe this would not be as replacement for the existing resolvers but something additional? If you would like I could draft something but I would need to understand how it's currently working

We're truly appreciate the possibility of you working on a draft for how we can incorporate support for Async in our resolvers and make it work.

i.e. the use of the global state is confusing me a lot - I'm sure there's a reason for it but I don't know why we're storing and accessing the event in a global manner.

This code is a little older, probably from when we created the Event Handler utility, but it is still in use. Payloads are different when working with Event Handler, and we store the event and context in the BaseRouter class because we need to access particular fields depending on the type of Resolver, i.e:

ALB - https://github.com/aws-powertools/powertools-lambda-python/blob/develop/tests/events/albEvent.json
VPC Lattice - https://github.com/aws-powertools/powertools-lambda-python/blob/develop/tests/events/vpcLatticeEvent.json
API Gateway REST - https://github.com/aws-powertools/powertools-lambda-python/blob/develop/tests/events/apiGatewayProxyEvent.json

All of our resolvers inherit from ApiGatewayResolver and ApiGatewayResolver inherits from BaseRouter (the Router class too), so we store this to be easily accessible globally across all Resolvers and dependencies.

Does this explanation cover what you need, or would you like more information? Feel free to ask if you need further clarification!

Is there chance for some guidance or rather I should stay put not to generate more work for you than needed?

Of course, there is! Please feel free to share any questions or blocks you have, and we can collaborate to find the best way to move forward.

Thank you for taking the time to collaborate on this matter! 🌟

33 remaining items

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestfeature requesthelp wantedCould use a second pair of eyes/handson-holdThis item is on-hold and will be revisited in the future

    Projects

    Status

    On hold

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @drecdroid@leandrodamascena@pkucmus@dreamorosi@sevetseh28

        Issue actions

          Feature request: Async support for ApiGatewayResolver · Issue #3934 · aws-powertools/powertools-lambda-python