Skip to content

Usage with Litestar

More advanced example of usage with Litestar - litestar-sqlalchemy-template

How to use

1. Install modern-di-litestar

uv add modern-di-litestar
pip install modern-di-litestar
poetry add modern-di-litestar

2. Apply to your application

import dataclasses

import litestar
import modern_di_litestar
from modern_di import Container, Group, Scope, providers


@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
class Settings:
    service_name: str = "catalog"


@dataclasses.dataclass(kw_only=True, slots=True)
class Report:
    settings: Settings   # APP-scoped, injected by type

    def as_dict(self) -> dict[str, str]:
        return {"service": self.settings.service_name}


class AppGroup(Group):
    settings = providers.Factory(Settings, scope=Scope.APP, cache=True)
    report = providers.Factory(Report, scope=Scope.REQUEST)


@litestar.get("/report", dependencies={"report": modern_di_litestar.FromDI(Report)})
async def get_report(report: Report) -> dict[str, str]:
    return report.as_dict()


app = litestar.Litestar(
    route_handlers=[get_report],
    plugins=[modern_di_litestar.ModernDIPlugin(Container(groups=[AppGroup], validate=True))],
)

Auto-wiring with autowired_groups

Pass autowired_groups to ModernDIPlugin to automatically register every provider in those groups as a Litestar dependency, keyed by its attribute name. This lets route handlers declare dependencies as plain parameters without per-route FromDI calls:

import dataclasses
import litestar
from modern_di import Container, Group, Scope, providers
from modern_di_litestar import ModernDIPlugin


@dataclasses.dataclass(kw_only=True)
class UserRepository:
    pass


class AppGroup(Group):
    user_repo = providers.Factory(UserRepository, scope=Scope.REQUEST)


ALL_GROUPS = [AppGroup]

app = litestar.Litestar(
    plugins=[ModernDIPlugin(Container(groups=ALL_GROUPS, validate=True), autowired_groups=ALL_GROUPS)],
)


@litestar.get("/users")
async def list_users(user_repo: UserRepository) -> list[str]:
    ...

If the same attribute name appears in multiple groups, a UserWarning is emitted and the last group's provider wins.

Websockets

Websockets add SESSION scope between APP and REQUEST — see the scope hierarchy. SESSION covers the lifetime of the websocket connection and is entered automatically; REQUEST covers one message and must be entered manually:

import dataclasses
import litestar
from modern_di import Container, Group, Scope, providers
import modern_di_litestar


@dataclasses.dataclass
class MyService:
    async def handle(self, data: str) -> None: ...


class Dependencies(Group):
    my_service = providers.Factory(MyService, scope=Scope.REQUEST)


ALL_GROUPS = [Dependencies]

app = litestar.Litestar(plugins=[modern_di_litestar.ModernDIPlugin(Container(groups=ALL_GROUPS, validate=True))])


@litestar.websocket_listener("/ws")
async def websocket_handler(
    data: str,
    di_container: Container,  # auto-resolved — the plugin registers a "di_container" dependency
) -> None:
    # For a websocket, di_container is the SESSION-scoped child; enter REQUEST scope here
    async with di_container.build_child_container(scope=Scope.REQUEST) as request_container:
        service = request_container.resolve(MyService)
        await service.handle(data)


app.register(websocket_handler)

di_container is injected by name — the plugin registers it as a Litestar dependency, so you don't need a FromDI marker for the container itself.

Framework Context Objects

Framework-specific context objects like litestar.Request and litestar.WebSocket are automatically made available by the integration — see Framework Context Objects for how implicit and explicit resolution work.

The following context providers are available for import:

  • litestar_request_provider - Provides the current litestar.Request object
  • litestar_websocket_provider - Provides the current litestar.WebSocket object

Implicit Usage (Type-based Resolution)

import litestar
from modern_di import Group, providers, Scope


def create_request_info(request: litestar.Request) -> dict[str, str]:
    return {
        "method": request.method,
        "url": str(request.url),
        "timestamp": "2023-01-01T00:00:00Z"
    }


class AppGroup(Group):
    # Factory automatically resolves the request dependency based on type annotation
    request_info = providers.Factory(
        create_request_info,
        scope=Scope.REQUEST,
    )

Explicit Usage (Provider-based Resolution)

import litestar
import modern_di_litestar
from modern_di import Group, providers, Scope


def create_request_info(request: litestar.Request) -> dict[str, str]:
    return {
        "method": request.method,
        "url": str(request.url),
        "timestamp": "2023-01-01T00:00:00Z"
    }


class AppGroup(Group):
    # Factory explicitly uses the request provider from the integration
    request_info = providers.Factory(
        create_request_info,
        scope=Scope.REQUEST,
        kwargs={"request": modern_di_litestar.litestar_request_provider}
    )

See also

API

Symbol Description
ModernDIPlugin(container, autowired_groups=None) Litestar InitPlugin that registers the container, composes the lifespan, and (if autowired_groups is given) exposes each provider in those groups as a Litestar dependency keyed by attribute name.
FromDI(dependency) Returns a Litestar Provide that resolves a provider or type from the per-request child container.
fetch_di_container(app) Returns the root Container stored on the Litestar app.
litestar_request_provider ContextProvider for litestar.Request (REQUEST scope), auto-registered.
litestar_websocket_provider ContextProvider for litestar.WebSocket (SESSION scope), auto-registered.