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.
Summary
Depends()(#8128) currently injectsRequestinto 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 thatappend_context/app.contextcurrently 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 andapp.contextextensively (~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) whichDepends()cannot do. However,Depends()does replace the data-sharing role that middleware performs viaappend_context/app.context. For that role, dependency functions need the same data access that middleware has.Why
Requestis a problematic injection target1. The abstraction is incomplete. The example in #8099 itself wrote
request.current_event— which doesn't exist onRequest.Requestonly 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.pyline 26, the handler usesapp.current_event.json_bodyvia a module global because there's no way to access it throughDepends().3. Module globals don't work across router modules. When routes are split into separate files using
Router(), the module-levelappis aRouterinstance, not the resolver — it doesn't havecurrent_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 ofDepends().5.
Requestinjection proves globals aren't the answer. If module globals were sufficient, there'd be no need forRequestinjection 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, andlambda_context. SinceDepends()replaces the data-sharing role of middleware, dependency functions need the same level of data access. Without it, teams can't incrementally migrate theirapp.contextusage toDepends().Proposed change
Replace
Requestinjection with resolver injection insolve_dependencies():BaseRouter(or any resolver subclass) automatically receive the resolverRequestinjection — users who wantRequestcan useapp.requestRequestclass stays as-is, just no longer auto-injectedBaseRouterfromaws_lambda_powertools.event_handlerBefore (current):
After (proposed):
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.