Skip to Content
NexusDI compared with InversifyJS, tsyringe, awilix and needle-di

This page documents the 0.4 release candidates of @nexusdi/core. The newest on npm is 0.4.0-rc.1. The documentation for 0.3.2, the current release, is at nexus.js.org. Try the release candidate.

NexusDI vs Other Containers

When choosing a dependency injection container, the primary trade-offs are typically bundle size, startup performance, and the ability to detect wiring mistakes.

NexusDI is compared here against four other TypeScript containers using a standardized benchmark harness. Every figure on this page is derived from actual measurements of the same eight-provider graph. The page renders each figure from the harness’s results files at build time. Understanding Benchmark Measurements states the method behind each one.

Performance and Footprint

The performance table below compares bundle size, cold-start time, and resolution speed.

  • Bundle Size: The minified and gzipped esbuild bundle size.
  • Startup Time: Cold start of a fresh Node process.
  • Resolve Speed: Time for a single transient resolution on a ready container.
  • Build Time: The library’s fastest passing build of a 200-class graph under a toolchain its documentation names.

Each library appears in the variant its own documentation teaches. Focus or hover a figure for its spread and, on another library’s row, the paired ratio to NexusDI with its interval. A ratio below 1 means NexusDI took less time, and an interval that contains 1 is no measured difference.

Performance comparison

Timings run 2026-10-01-8824502 on INTEL(R) XEON(R) PLATINUM 8573C, with @nexusdi/core 0.4.0-rc.0. Each figure is a median; focus or hover it for the spread. How the benchmarks are measured
LibraryBundle size (min+gzip)StartupResolveBuild time
NexusDI plain19.0 kBminified 57.5 kB53.1 msMAD 2.01 msp5 49.5 msp95 58.4 msready 152 µs124 nsMAD 2.5 nsp5 120 nsp95 142 ns264 ms (bun)MAD 2.98 msp5 259 msp95 269 ms
InversifyJS decorated22.0 kBminified 91.7 kBdecorator metadata polyfill 4.7 kB of it81.8 msMAD 1.94 msp5 77.6 msp95 87.7 msready 165 µsNexusDI ÷ this: 0.65× (95% interval 0.65× to 0.65×)66.3 nsMAD 1.12 nsp5 64.1 nsp95 75.2 nsNexusDI ÷ this: 1.87× (95% interval 1.87× to 1.88×)518 ms (tsgo)MAD 4.35 msp5 512 msp95 530 msNexusDI ÷ this: 0.51× (95% interval 0.50× to 0.51×)
tsyringe decorated9.8 kBminified 30.6 kBdecorator metadata polyfill 4.7 kB of it34 msMAD 1.31 msp5 31.8 msp95 38.3 msready 2.54 µsNexusDI ÷ this: 1.55× (95% interval 1.54× to 1.56×)193 nsnoisyMAD 15.5 nsp5 167 nsp95 224 nsNexusDI ÷ this: 0.68× (95% interval 0.67× to 0.68×)486 ms (tsgo)MAD 7.11 msp5 477 msp95 505 msNexusDI ÷ this: 0.54× (95% interval 0.53× to 0.54×)
awilix plain3.9 kBminified 10.1 kB43.6 msMAD 1.17 msp5 41.3 msp95 47.4 msready 61.5 µsNexusDI ÷ this: 1.21× (95% interval 1.21× to 1.22×)131 nsnoisyMAD 7.06 nsp5 122 nsp95 153 nsNexusDI ÷ this: 0.96× (95% interval 0.96× to 0.97×)261 ms (bun)MAD 2.39 msp5 256 msp95 266 msNexusDI ÷ this: 1.02× (95% interval 1.00× to 1.02×)
needle-di decorated3.4 kBminified 10.5 kB27.7 msMAD 1 msp5 25.6 msp95 31.2 msready 12.9 µsNexusDI ÷ this: 1.92× (95% interval 1.91× to 1.93×)not measured: needle-di documents singletons only.263 ms (bun)MAD 2.17 msp5 260 msp95 270 msNexusDI ÷ this: 1.00× (95% interval 0.99× to 1.01×)

Toolchain Flexibility

Many DI libraries rely on emitDecoratorMetadata, which requires specific compiler flags that not every toolchain supports. In the matrix below, the metadata-based variants fail to compile under Node’s type stripping, and tsyringe’s fails at runtime under esbuild. Every variant passes under Bun and Deno.

NexusDI avoids this by using static deps. Because static deps needs no compiler-emitted metadata, it passes under every toolchain in the matrix. The @nexusdi/decorators variant uses standard decorators, which plain Vite and Node’s type stripping cannot compile.

A cell that does not pass keeps its first error line in a tooltip and links the toolchain’s own page when that page documents the limitation. InversifyJS’s getting-started page  and tsyringe’s README  set experimentalDecorators and emitDecoratorMetadata for their decorated setups. awilix  and needle-di  document setups that use no decorator metadata (inversify 8.2.3, tsyringe 4.10.0, awilix 13.0.5, needle-di 1.2.1; read 2026-09-30).

Toolchain matrix

The outcome of Meridian-8 for each library and variant under each toolchain, with @nexusdi/core 0.4.0-rc.0.
Library and varianttsctsgoesbuildswcbabelvitevite8+babel-pluginbundenonode-strip-types
awilix plainpasspasspasspasspasspasspasspasspasspass
InversifyJS decoratedpasspasspasspasspasspasspasspasspassWarning experimentalDecorators compiler option is deprecated and may be removed at any timecompile errorSyntaxError: Invalid or unexpected token documented
InversifyJS decorated-explicitpasspasspasspasscompile errorSyntaxError: Invalid or unexpected tokenpasscompile errorerror during build: Build failed with 7 errors: [PARSE_ERROR] Decorators are not valid here.passpassWarning experimentalDecorators compiler option is deprecated and may be removed at any timecompile errorSyntaxError: Invalid or unexpected token documented
InversifyJS plainpasspasspasspasspasspasspasspasspasspass
needle-di decoratedpasspasspasspasspasscompile errorSyntaxError: Invalid or unexpected token documentedpasspasspasscompile errorSyntaxError: Invalid or unexpected token documented
needle-di plainpasspasspasspasspasspasspasspasspasspass
NexusDI decoratedpasspasspasspasspasscompile errorSyntaxError: Invalid or unexpected token documentedpasspasspasscompile errorSyntaxError: Invalid or unexpected token documented
NexusDI plainpasspasspasspasspasspasspasspasspasspass
tsyringe decoratedpasspassruntime errorError: TypeInfo not known for "QuantumComputer" documentedpasspasspasspasspasspassWarning experimentalDecorators compiler option is deprecated and may be removed at any timecompile errorSyntaxError: Invalid or unexpected token documented
tsyringe decorated-explicitpasspasspasspasscompile errorSyntaxError: Invalid or unexpected tokenpasscompile errorerror during build: Build failed with 8 errors: [PARSE_ERROR] Decorators are not valid here.passpassWarning experimentalDecorators compiler option is deprecated and may be removed at any timecompile errorSyntaxError: Invalid or unexpected token documented
tsyringe plainpasspasspasspasspasspasspasspasspasspass

Wiring-Mistake Detection

Most containers are “optimistic”. They assume the graph is correct and only fail at runtime when a missing dependency is requested. This can lead to crashes in production on rarely-used code paths.

NexusDI is “pessimistic.” It validates the entire module graph during the Nexus.create phase. If a dependency is missing, Nexus.create reports it before any constructor runs. One error lists every problem it found (Errors).

The probe table adds one mistake at a time to the graph and shows where each library reports it.

Wiring-mistake probes

When each library reports a wiring mistake in Meridian-8, with @nexusdi/core 0.4.0-rc.0.
MistakeNexusDIInversifyJStsyringeawilixneedle-di
A provider is never registeredcontainer creationMissingProviderError: [NEXUS_MISSING_PROVIDER] token=NavCharts requester=Bridge module=Meridian. https://nexus.js.org/errors/NEXUS_MISSING_PROVIDERfirst resolveError: No bindings found for service: "Symbol(NavCharts)".first resolveError: Cannot inject the dependency "charts" at position #1 of "Bridge" constructor. Reason: Attempted to resolve unregistered dependency token: "NavCharts"first resolveAwilixResolutionError: Could not resolve 'charts'.first resolveError: No provider(s) found for InjectionToken "NavCharts"
Two providers depend on each othercontainer creationCircularDependencyError: [NEXUS_CIRCULAR_DEPENDENCY] path=PowerRouter,ShieldGrid,PowerRouter. https://nexus.js.org/errors/NEXUS_CIRCULAR_DEPENDENCYfirst resolveError: Circular dependency found: Bridge -> ShieldGrid -> PowerRouter -> ShieldGridneverfirst resolveAwilixResolutionError: Could not resolve 'shield'. Cyclic dependencies detected.first resolveError: Detected circular dependency: Bridge -> ShieldGrid -> PowerRouter -> ShieldGrid. Please change your dependency graph or use lazy injection instead.
A singleton depends on a scoped providercontainer creationLifetimeError: [NEXUS_LIFETIME_VIOLATION] path=Bridge,FlightLog lifetimes=singleton,scoped. https://nexus.js.org/errors/NEXUS_LIFETIME_VIOLATIONnot applicableneverfirst resolveAwilixResolutionError: Could not resolve 'flightLog'. Dependency 'flightLog' has a shorter lifetime than its ancestor: 'bridge'not applicable
A dependency has the wrong typetype checksrc/main.ts - error TS2769: No overload matches this call.nevernevertype checksrc/main.ts - error TS2322: Type 'PowerRouter' is not assignable to type 'FusionReactor'.never
A missing provider and a cycle togethercontainer creation, names 2 of 2MissingProviderError: [NEXUS_MISSING_PROVIDER] token=NavCharts requester=Bridge module=Meridian. https://nexus.js.org/errors/NEXUS_MISSING_PROVIDERfirst resolve, names 1 of 2Error: No bindings found for service: "Symbol(NavCharts)".first resolve, names 1 of 2Error: Cannot inject the dependency "charts" at position #1 of "Bridge" constructor. Reason: Attempted to resolve unregistered dependency token: "NavCharts"first resolve, names 1 of 2AwilixResolutionError: Could not resolve 'charts'.first resolve, names 1 of 2Error: No provider(s) found for InjectionToken "NavCharts"

Lifecycle and Disposal Comparison

Managing resources (like database pools) is critical. NexusDI implements a strict reverse-creation order for disposal and uses SuppressedError to ensure every resource is closed, even if some failures occur. Lifecycle and Disposal explains why each row matters.

A cell says what the library does without extra plugins unless it names one.

FeatureNexusDIInversifyJStsyringeawilixneedle-diTypeDINestJS
Dependency-ordered onInitYesPer instance, on first resolveNoNoNoNoYes
Reverse-order DisposalYesNoNoNon/aNoYes
Reports all disposal failuresYesNoNoNon/aNoLogged
Scopes own transientsYesNoYesNon/aNoNo
Disposes factory resultsYesPartialNoWith .disposer()n/aYesYes

The harness does not measure this table: it is read from each library’s source and documentation, and the notes are in specs/2026-10-03-lifecycle-comparison-research.md.

The detailed version of the same comparison, row by row:

NexusDIInversifyJStsyringeawilixneedle-diTypeDINestJS
Startup hook awaited in dependency orderYes, onInit before create resolvesPer instance on first resolve, awaited through getAsyncNo hookNo hookNo hookNo hookYes, onModuleInit
Container disposalSymbol.asyncDisposeunbindAllAsync()dispose()dispose()Nonereset() calls destroy()app.close()
Supports Symbol.dispose and Symbol.asyncDisposeBothBoth, with @inversifyjs/plugin-disposeNoNoNoNoNo
Disposal orderReverse creationUnspecified, plugin runs fewest dependents firstCreation order, so dependencies firstAll at oncen/aRegistration orderReverse of startup
All disposer failures reportedYes, SuppressedError chainNo, first errorNo, first errorNo, first errorn/aNo, errors ignoredLogged, not thrown
Keeps going after a failureYesPlugin stops at the firstAsync failures only, a sync throw skips the restYesn/aYesYes
Scopes own transientsYesNoYes, by the container that resolved itNon/aNoNo, request-scoped providers get no hooks
Disposes factory resultsYesOnly with onDeactivationNoYes, with .disposer()n/aYesYes
Leaves useValue objects aloneYesYes, unless onDeactivation is setYesYesn/aYesNo, hooks run on them

NestJS has the most complete startup story of the six. It awaits hooks module by module and by dependency depth inside a module, runs shutdown in the reverse order and logs every failure. Its hooks are provider methods, and a Nest application is not an AsyncDisposable. tsyringe disposes every disposable instance it builds, transients included. awilix awaits any promise a .disposer() returns, and a container disposes only its own cache, never its scopes. InversifyJS has @postConstruct and @preDestroy, and with its dispose plugin it supports await using. needle-di and TypeDI document no startup hook and no awaited disposal.

Versions read on 2026-10-03: inversify 8.2.3 (with @inversifyjs/plugin-dispose 0.4.7), tsyringe 4.10.0, awilix 13.0.5, needle-di 1.2.1, TypeDI 0.10.0, @nestjs/core 12.1.2. Sources: the published packages’ code, InversifyJS container API , tsyringe disposable instances , awilix disposing , needle-di docs  and NestJS lifecycle events .

Containers Left Out

TypeDI is left out of the measurements: its last release, 0.10.0, was published on 2021-01-15 (npm ). NestJS is a framework with its own container, so it is left out too.

Conclusion

If you are building a large-scale application where reliability, startup speed, and toolchain flexibility are priorities, NexusDI’s static validation and lightweight core make it a strong choice. For those already using the NestJS framework, its built-in container already wires your services, and NexusDI would add a second container beside it. See When to skip NexusDI.

Break and Fix a Benchmark Figure

Every figure on this page comes from the harness, and a guard keeps it that way. The <Figure> component renders one result inline in a sentence, and the doc-benchmark-figures guard fails on a path the results do not hold.

Try it: In apps/docs/content/index.mdx, change the figure’s path from size.nexusdi.plain.esbuild.gzip to size.nexusdi.plain.esbuild.brotli, and run the guard:

npx nx test @nexusdi/repo-checks -- doc-benchmark-figures

The guard fails and names the key the results lack. Type a size with its unit into a sentence on the same page, and the guard fails on that sentence too, because a figure in prose comes only from the harness. Restore the path and delete the sentence, and the guard passes.

How This Comparison Was Measured

The NexusDI maintainer wrote the harness and measured these results, and has an interest in them. Each library’s fixture follows its own documentation, and Understanding Benchmark Measurements lists what was pinned, what was tested, what was left out, and how to report a fixture that departs from a library’s documentation.

Measured with

Libraries
NexusDI 0.4.0-rc.0InversifyJS 8.2.3 with a decorator metadata polyfilltsyringe 4.10.0 with a decorator metadata polyfillawilix 13.0.5needle-di 1.2.1
Toolchains
tsc 6.0.3tsgo 7.0.2esbuild 0.28.2swc 1.16.2babel 8.0.6vite 8.3.0vite8+babel-plugin 8.3.0bun 1.4.2deno 2.9.6node-strip-types 24.20.0bundlers: esbuild 0.28.2, rollup 4.63.4
Runtime
Node 24.20.0
Runner
INTEL(R) XEON(R) PLATINUM 8573C, 4 cores, 16 GB, Linux, GitHub-hosted
Timings run
2026-10-01, seed 36891702544, core commit 8824502
Results files
matrix.json at a9231afprobes.json at a9231afsize.json at f699eb1build.json at 20d5e27timings/2026-10-01-8824502.json at 20d5e27