Introduction
@nodelibraries/ioc is a lightweight, type-safe Inversion of Control (IoC) container for Node.js and TypeScript, inspired by .NET Core's dependency injection system. It provides dependency injection capabilities similar to .NET's built-in DI container, but designed specifically for TypeScript/JavaScript.
@nodelibraries/ioc - A lightweight, type-safe IoC container designed for Node.js and TypeScript. Learn more in our About page.
Key Features
- 🎯 Type-safe - Full TypeScript support with type inference
- 📜 JavaScript Support - Works with both TypeScript and JavaScript
- 🔄 Multiple Lifetimes - Singleton, Scoped, and Transient service lifetimes
- 🏗️ Dependency Injection - Automatic dependency resolution
- 📦 Lightweight - Zero dependencies
- 🎨 Flexible Registration - Register both concrete classes and abstract interfaces
- 🚫 No Decorators - Clean code without decorator pollution
- 🏭 Factory Pattern - Support for factory functions and async initialization
- 🔢 Multiple Implementations - Register and retrieve multiple implementations
- 🔑 Keyed Services - Key-based service lookup (with getRequiredKeyedService)
- ✅ TryAdd Pattern - Safe registration without overriding
- 🛡️ Scope Validation - Detect lifetime mismatches at build time (validateScopes & validateOnBuild)
- 🔄 Circular Dependencies - Automatic resolution of circular dependencies for all lifetimes
- 🔍 Service Checking - Check service existence with isService() without resolving
- 🗑️ Service Management - Remove, replace, and manage services dynamically
- 🔄 Lifecycle Hooks - onInit() and onDestroy() callbacks for service initialization and cleanup
- 💎 Value Registration - Register pre-created values (JSON, primitives, instances)
What is Dependency Injection?
Dependency Injection (DI) is a design pattern where objects receive their dependencies from an external source rather than creating them internally. This makes your code more modular, testable, and maintainable.
Without Dependency Injection
class UserService {
private logger = new Logger(); // Tight coupling!
getUsers() {
this.logger.log('Fetching users...');
return [];
}
}With Dependency Injection
class UserService {
constructor(private logger: ILogger) {} // Loose coupling!
getUsers() {
this.logger.log('Fetching users...');
return [];
}
}Why Use an IoC Container?
An IoC container manages the creation and lifetime of your services, automatically resolving dependencies. This means:
- Less boilerplate - No need to manually wire dependencies
- Better testability - Easy to mock dependencies in tests
- Lifetime management - Automatic handling of singleton, scoped, and transient services
- Type safety - Compile-time checking of dependencies
Comparison with .NET DI
This container is inspired by .NET's dependency injection system. It provides similar features:
- ✅ Singleton, Scoped, Transient lifetimes
- ✅ Factory pattern support
- ✅ Multiple implementations
- ✅ Keyed services (with getRequiredKeyedService)
- ✅ TryAdd pattern
- ✅ Scope validation (validateScopes & validateOnBuild)
- ✅ Service checking (isService)
- ✅ Service management (remove, removeAll, replace)
- ✅ Lifecycle hooks
- ✅ Value registration
- ✅ Circular dependency support
For a detailed comparison, see the COMPARISON.md file in the repository.