Skip to content

feat(event_handler): inject resolver into Depends() instead of Request for middleware parity #8139

@Iamrodos

Description

@Iamrodos

Summary

Depends() (#8128) currently injects Request into dependency functions — a thin 7-property wrapper that loses most of what Powertools provides. Before this ships in a release, I would like to propose the injection target should be the resolver (app) instead. This gives dependency functions the same data access that middleware has, and enables incremental migration of the data-sharing role that append_context/app.context currently fills.

I have a PR ready to accompany this issue.

Context

This comes from real-world evaluation of adopting Depends() in an existing codebase that uses middleware and app.context extensively (~550 references). See discussion #4551 (my detailed feedback comment) and the original feature request #8099.

Note: Depends() is not a replacement for middleware — middleware handles request interception (short-circuiting with 401s, modifying responses) which Depends() cannot do. However, Depends() does replace the data-sharing role that middleware performs via append_context/app.context. For that role, dependency functions need the same data access that middleware has.

Why Request is a problematic injection target

1. The abstraction is incomplete. The example in #8099 itself wrote request.current_event — which doesn't exist on Request. Request only exposes 7 properties: route, path_parameters, method, headers, query_parameters, body, json_body. Missing: cookies, actual request path, event helpers (get_query_string_value, get_header_value), request_context, and the full Lambda event.

2. The docs already work around the gap. In examples/event_handler_rest/src/dependency_injection.py line 26, the handler uses app.current_event.json_body via a module global because there's no way to access it through Depends().

3. Module globals don't work across router modules. When routes are split into separate files using Router(), the module-level app is a Router instance, not the resolver — it doesn't have current_event. This is the exact problem that started discussion #4551 and led to the middleware wrapper pattern. Using app instead of Request unlocks much more sophisticated split routing scenarios without workarounds.

4. Module globals break dependency_overrides. If dependency functions read from globals instead of receiving the resolver as a parameter, you can't swap them in tests. This undermines the core testability value of Depends().

5. Request injection proves globals aren't the answer. If module globals were sufficient, there'd be no need for Request injection at all. The intention of Request indicates that dependency functions should receive what they need as parameters. My suggestion is that the injected object just needs to be more capable.

6. Data access parity with middleware. Middleware receives the full resolver with current_event, context, and lambda_context. Since Depends() replaces the data-sharing role of middleware, dependency functions need the same level of data access. Without it, teams can't incrementally migrate their app.context usage to Depends().

Proposed change

Replace Request injection with resolver injection in solve_dependencies():

  • Dependency functions that type-hint a parameter as BaseRouter (or any resolver subclass) automatically receive the resolver
  • Remove the special-case Request injection — users who want Request can use app.request
  • Request class stays as-is, just no longer auto-injected
  • Export BaseRouter from aws_lambda_powertools.event_handler

Before (current):

def get_authenticated_user(request: Request) -> str:
    user_id = request.headers.get("x-user-id")  # limited to 7 properties
    # Can't access: cookies, path, app.context, current_event helpers
    return user_id

After (proposed):

from aws_lambda_powertools.event_handler import BaseRouter

def get_authenticated_user(app: BaseRouter) -> str:
    user_id = app.current_event.headers.get("x-user-id")
    # Full access: cookies, path, context, event helpers, lambda_context
    return user_id

This is a minimal change — 3 files modified in the core, plus test and doc updates. I have created a PR for review and look forward to a discussion to bring the best we can to Powertools users.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions