Framework comparison
Choose the smallest wiring tool that fits.
Injex is intentionally small. It is not trying to replace every Python DI framework. It fits the middle ground between manual wiring and a large provider framework.
Where Injex loses
Pick a larger framework when you need this — Injex deliberately doesn't do it:
- A configuration/provider DSL. Loading config from
env/files, coercing types, and wiring values through provider objects is
dependency-injector's domain. In Injex configuration is a normal dependency: register a settings object, or values as named registrations and inject them withAnnotated[T, Named(...)].
Things people assume Injex can't do, but it can: it manages async and sync
resource lifecycles via ascope() / create_scope(),
injects into FastAPI routes through
injex.ext.fastapi, and
injects into any function (Typer/Click commands, workers) through
container.call().
For the general manual-vs-framework-vs-Injex decision, see the comparison guide.
Dependency Injector
dependency-injector is mature and feature-rich. It is a strong
fit when provider objects, configuration providers, and explicit
container classes are part of the application architecture.
Injex is a better fit when the desired API is smaller: register normal classes, validate the graph, resolve services, and keep constructors plain.
Wireup and Dishka
Wireup and Dishka focus on modern autowiring and framework integration. They can be a good choice when the container should participate directly in FastAPI or task framework scopes.
Injex keeps the boundary more conservative: build the app graph at the composition root, then let FastAPI, Typer, and workers adapt that graph at their edge.
Benchmarks in Injex docs are graph-specific sanity checks, not universal DI rankings.