I'm working on a sketch of a project using Litestar and I want to leverage the dependency-injector
framework by Roman Mogylatov to handle dependency injection within my controllers. However, I've encountered some issues when trying to integrate dependency-injector
with Litestar’s controller classes. I could do this with FastAPI because of the nature of their Depends
function, but with Litestar nothing worked. I'm actually searching for someone with experience on this, because there's no documentation on integrating this kind of annotated dependency injection with Litestar framework. With the few months of experience with this kind of frameworks, I find dependency-injector an ideal implementation because it provides lazy initialization and more reliability through design patterns. I also don't like the function-based dependency injection most python web frameworks suggest since I find them not organized. Despite reading the documentation for both Litestar and dependency-injector
, I haven't found a solution that works.
Here's an overview of my project structure:
lite/
├── __init__.py
├── __main__.py
├── controllers.py
├── di.py
├── logging.py
├── main.py
├── services.py
di.py
from dependency_injector import containers, providers
from .services import Service
class Container(containers.DeclarativeContainer):
service = providers.Factory(Service)
__main__.py
:import uvicorn
from .di import Container
from .logging import logger
if __name__ == "__main__":
try:
container = Container()
container.wire(modules=[__name__,
"lite",
"lite.controllers",])
config = uvicorn.Config(app="lite.main:app", port=3000)
server = uvicorn.Server(config)
server.run()
except Exception as e:
logger.exception(e)
main.py
:from litestar import Litestar
from .controllers import ExampleController
from litestar.logging import LoggingConfig
app = Litestar(
route_handlers=[ExampleController],
logging_config=LoggingConfig(
root={"level": "INFO", "handlers": ["queue_listener"]},
formatters={
"standard": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
},
log_exceptions="always",
),
)
Here is how I've tried to use dependency injection in my example controller:
controllers.py
:from typing import Annotated
from litestar import Controller, get
from litestar.di import Provide as Depends
from litestar.params import Dependency
from .di import Container
from dependency_injector.wiring import Provide, inject
from .services import Service
class ExampleController(Controller):
path = "/test"
@get("/")
@inject
async def bar(self, service: Service = Depends(Provide[Container.service])) -> None:
service.foo() # just prints "foo"
Service
dependency to be injected into the bar
method in ExampleController
using dependency-injector
.Service.foo()
method should be called successfully without any exceptions.When I run the application, I encounter the following exception:
Traceback (most recent call last):
File "/workspaces/LiteTest/.venv/lib/python3.10/site-packages/litestar/_signature/model.py", line 101, in _deserializer
return default_deserializer(target_type, value)
File "/workspaces/LiteTest/.venv/lib/python3.10/site-packages/litestar/serialization/msgspec_hooks.py", line 139, in default_deserializer
raise TypeError(f"Unsupported type: {type(value)!r}")
TypeError: Unsupported type: <class 'dependency_injector.wiring.Provide'>
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/workspaces/LiteTest/.venv/lib/python3.10/site-packages/litestar/_signature/model.py", line 203, in parse_values_from_connection_kwargs
return convert(kwargs, cls, strict=False, dec_hook=deserializer, str_keys=True).to_dict()
msgspec.ValidationError: Unsupported type: <class 'dependency_injector.wiring.Provide'> - at `$.service`
...
dependency-injector
documentation, but they don't provide guidance for this specific scenario.How can I properly use dependency-injector
to inject dependencies into methods of a Litestar Controller
subclass? Is there a way to make the dependency-injector
compatible with Litestar’s dependency injection system, or do I need to implement a workaround?
Any examples or suggestions would be greatly appreciated!
I discovered a very dirty way to integrate these two:
from litestar import Controller, get
from litestar.di import Provide as Depends
from .di import Container, Provide, inject
from .services import Service
@inject
def _get_service(service: Service = Provide[Container.service]) -> Service:
return service
def get_service() -> Service:
return _get_service()
class ExampleController(Controller):
path = "/test"
@get("/", dependencies={"service": Depends(get_service)})
async def bar(self, service: Service) -> None:
service.foo()
I can suggest an example with dishka
instead of dependency-injector
. From my point of view it gives you more flexibility