InversifyJS
A lightweight, decorator-driven IoC container that brings SOLID-friendly dependency injection to TypeScript and JavaScript apps.
Repository Health
Technical Analysis
InversifyJS is an inversion of control (IoC) container for TypeScript and JavaScript applications. It uses a class constructor to identify a component’s dependencies and inject them automatically, replacing manual new wiring with declarative @injectable()/@inject() decorators and a fluent container.bind(...).to(...) API.
The project is maintained as a monorepo (inversify/monorepo) with the inversify package as its flagship container, built on shared internal building blocks (@inversifyjs/core, @inversifyjs/container, @inversifyjs/common) that are also reused by companion packages for HTTP framework integration, validation, logging, and OpenAPI generation. This split keeps the core container dependency-free and small while letting the ecosystem grow around it.
InversifyJS is widely used in Node.js backends, CLI tools, and any TypeScript codebase that wants class-based, constructor-injected dependencies without hand-rolling a service locator. It has shipped for over a decade, is used by teams at Microsoft, AWS Amplify, Elastic, and Slack, and continues to see active weekly releases.
What You Get
- A
Containerclass with a fluentbind<T>(id).to(Klass)/.toConstantValue()/.toDynamicValue()API for registering services @injectable()and@inject()decorators (plus@multiInject,@named,@tagged,@optional) for declaring constructor and property dependencies- Binding scopes (singleton, transient, request/resolution-scoped) to control instance lifecycle
ContainerModulefor organizing bindings into composable, reusable units- Hierarchical containers with parent/child lookup for scoping bindings across app boundaries
- Activation/deactivation hooks and a plugin system (
@inversifyjs/plugin) for cross-cutting concerns like disposal
Common Use Cases
- Wiring service, repository, and controller layers in a Node.js backend without manual constructor wiring
- Swapping real implementations for test doubles by rebinding a service identifier in unit tests
- Building a plugin-friendly architecture where features register their own bindings via
ContainerModule - Powering the routing/handler layer of the companion
@inversifyjs/http-*framework packages (Express, Fastify, Hono, uWebSockets) - Scoping request-lifetime instances in web servers via resolution/request scope bindings
Under The Hood
Architecture
The inversify package itself is a thin composition layer: src/index.ts re-exports the public API almost entirely from two internal workspace packages, @inversifyjs/container (the Container class, binding models, activation/deactivation actions, calculations for resolving constraints) and @inversifyjs/core (decorators, metadata reflection, planning/resolution engine, binding services), with shared primitives coming from @inversifyjs/common. Resolution follows a plan-then-execute model: decorator metadata attached via reflect-metadata is read by the planning module to build a dependency graph, which the resolution module then walks to construct instances, applying binding constraints (named/tagged/conditional) and scope rules along the way. Hierarchical containers layer child/parent lookup on top of this, and a small plugin system (onActivation/onDeactivation, @inversifyjs/plugin-dispose) hooks into the binding lifecycle for cross-cutting concerns like resource cleanup. Because the container core is isolated from any single package, the same engine is reused unmodified by the HTTP integration packages (Express, Fastify, Hono, uWebSockets) elsewhere in the monorepo.
Tech Stack
The package is pure TypeScript (targeting type: module/ESM output via tsc), with reflect-metadata as its only meaningful runtime dependency for decorator metadata and no other production dependencies beyond its own workspace packages. The monorepo is managed with pnpm workspaces and Turborepo for task orchestration, uses Vitest (with a custom @inversifyjs/foundation-vitest-config preset and Stryker for mutation testing) as the test runner, ESLint/Prettier via shared @inversifyjs/foundation-* configs for linting and formatting, and Changesets for versioned releases across the many published sub-packages.
Code Quality
The inversify package’s src/test/ directory contains extensive integration specs (*.int.spec.ts) covering core features (request scope, property injection, transitive bindings, named/default bindings) plus a dedicated bugs/ directory with regression tests tied to specific GitHub issue numbers, indicating a project that codifies fixes as permanent tests. Each sibling package (core, container, common) mirrors this with its own vitest.config.mjs and a parallel vitest.config.stryker.mjs for mutation testing, and CI (.github/workflows/build.yaml, collect-coverage.yaml) runs build, lint, and coverage collection across the workspace. TypeScript strictness plus ESLint/Prettier enforcement via lint-staged/Husky pre-commit hooks keeps style consistent across the many packages.
API Design
The public API favors decorators and fluent chains over configuration objects: @injectable()/@inject() on classes, then container.bind<T>(id).to(Impl).inSingletonScope() for registration, which keeps most application code free of container-specific imports beyond the decorators themselves. Boilerplate to get started is minimal (two decorators and a bind/get pair), and the same container primitives extend cleanly into named/tagged bindings, container modules, and hierarchical containers without introducing a second, incompatible API surface.
Used by 2 apps in this directory
Kibana
Analytics · Monitoring
Your open source window into the Elastic Stack — query, visualize, and act on data stored in Elasticsearch with real-time dashboards, AI-assisted search, and automated alerting.
overleaf
Collaboration · Productivity
Open-source, real-time collaborative LaTeX editor with sandboxed compilation and full TeXLive support for self-hosted academic and research teams.