modern-di integration for Starlette.
Full guide: Starlette integration docs
Usage example: examples/
Installation
uv add modern-di-starlette # or: pip install modern-di-starlette
Usage
setup_di registers the container, composes the lifespan, and installs middleware that builds a per-connection child container. Decorate an endpoint with @inject and mark parameters with FromDI to receive resolved dependencies. Starlette has no native DI, so @inject is required (there is no Depends).
import dataclasses
import typing
from modern_di import Container, Group, Scope, providers
from modern_di_starlette import FromDI, inject, setup_di
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
@dataclasses.dataclass(kw_only=True)
class Settings:
debug: bool = True
@dataclasses.dataclass(kw_only=True)
class UserService:
settings: Settings # auto-injected by type
class Dependencies(Group):
settings = providers.Factory(scope=Scope.APP, creator=Settings)
user_service = providers.Factory(scope=Scope.REQUEST, creator=UserService)
@inject
async def homepage(
request: Request,
service: typing.Annotated[UserService, FromDI(Dependencies.user_service)],
) -> JSONResponse:
return JSONResponse({"debug": service.settings.debug})
app = Starlette(routes=[Route("/", homepage)])
container = Container(groups=[Dependencies])
setup_di(app, container)
container.validate() # optional fail-fast; must come after setup_di registers its providers
Call setup_di once, after creating the app and before it starts serving. It installs middleware, and Starlette does not allow middleware to be added after startup.
@inject works the same on the methods of a class-based endpoint. Decorate the handler method, not the class; self and any arguments Starlette passes after the connection are forwarded unchanged, so WebSocketEndpoint.on_receive and on_disconnect inject too:
from starlette.endpoints import HTTPEndpoint, WebSocketEndpoint
from starlette.routing import Route, WebSocketRoute
from starlette.websockets import WebSocket
class Users(HTTPEndpoint):
@inject
async def get(
self,
request: Request,
service: typing.Annotated[UserService, FromDI(Dependencies.user_service)],
) -> JSONResponse:
return JSONResponse({"debug": service.settings.debug})
class Echo(WebSocketEndpoint):
encoding = "text"
@inject
async def on_receive(
self,
websocket: WebSocket,
data: str,
settings: typing.Annotated[Settings, FromDI(Dependencies.settings)],
) -> None:
await websocket.send_text(f"{data} debug={settings.debug}")
app = Starlette(routes=[Route("/users", Users), WebSocketRoute("/echo", Echo)])
An HTTP request opens a Scope.REQUEST child container; a WebSocket connection opens a Scope.SESSION one, both built by the middleware before your handler runs. A WebSocket handler can therefore inject APP- and SESSION-scoped dependencies but not REQUEST-scoped ones. The connection starlette.requests.Request / starlette.websockets.WebSocket are resolvable within DI via the pre-built starlette_request_provider / starlette_websocket_provider context providers. The instance a provider receives is backed by the same ASGI scope as your handler's connection but is a distinct object: read method / url / headers / state from it, not the request body.
API
| Symbol | Description |
|---|---|
setup_di(app, container) |
Registers the container on app.state, composes the lifespan (opens/closes the container), and installs the middleware that builds a per-connection child container; returns the container |
FromDI(dependency) |
Inert marker (used with @inject) that resolves a provider or type from the per-connection child container |
inject(handler) |
Decorator for an async def handler taking a Request or WebSocket, either a function endpoint or a method of an HTTPEndpoint / WebSocketEndpoint subclass; resolves its FromDI-annotated parameters |
fetch_di_container(app) |
Returns the root Container stored on app.state |
starlette_request_provider |
ContextProvider for starlette.requests.Request (REQUEST scope), auto-registered |
starlette_websocket_provider |
ContextProvider for starlette.websockets.WebSocket (SESSION scope), auto-registered |
📦 PyPI
📝 License
Part of modern-python
Built on modern-di, a dependency-injection framework with an IoC container and scopes.
Browse the full list of templates and libraries in
modern-python; the org profile has the categorized index.
Metadata
Release files for modern-di-starlette 4.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| modern_di_starlette-4.0.0.tar.gz | 5.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| modern_di_starlette-4.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 12.0 kB
Release files / modern_di_starlette-4.0.0.tar.gz
| Download URL | modern_di_starlette-4.0.0.tar.gz |
|---|---|
| Size | 5.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c024db2cef36af0efd68f52c0c4f90d081720f0067c4a814efe305517c5230ad
|
|
BLAKE2b-256 checksum How to use checksums |
abf8f7c9d060f114dd081836e4f48148b856acb8b1c0b966104230d151391157
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.24 {"installer":{"name":"uv","version":"0.12.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / modern_di_starlette-4.0.0-py3-none-any.whl
| Download URL | modern_di_starlette-4.0.0-py3-none-any.whl |
|---|---|
| Size | 6.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
93cabcbf753729934090bc91bc3a17ed955c5fcf77a6a13fcf47671f5b0a5bde
|
|
BLAKE2b-256 checksum How to use checksums |
818448272ae1d8d19b5422ae89c6a1d243ddeee0f02525b8a94c3692e6b97717
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.24 {"installer":{"name":"uv","version":"0.12.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|