@stevanfreeborn/netdi v1.0.1
netdi
A dependency injection container for TypeScript projects that is inspired by the .NET DI container. It is designed to be simple, lightweight, and easy to use.
Features
- ✅ .NET-style service registration with different lifetimes (singleton, scoped, transient)
- ✅ Constructor injection with decorators
- ✅ Type-safe dependency resolution
- ✅ Factory method support for complex instantiation scenarios
- ✅ Scoped service lifetimes for per-request contexts
Installation
# npm
npm install @stevanfreeborn/netdi
# yarn
yarn add @stevanfreeborn/netdi
# pnpm
pnpm add @stevanfreeborn/netdi
Requirements
- Node.js >= 18.0.0
- TypeScript with decorators and reflection metadata enabled
Add the following to your tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Basic Usage
Here's a simple example of how to use netdi:
import {
ServiceCollection,
createServiceIdentifier,
injectable,
inject,
} from '@stevanfreeborn/netdi';
// Define interfaces
interface IGreeter {
greet(name: string): string;
}
interface IGreetingService {
createGreeting(name: string): string;
}
// Create service identifiers
const greeterIdentifier = createServiceIdentifier<IGreeter>();
const greetingServiceIdentifier = createServiceIdentifier<IGreetingService>();
// Implement services
@injectable()
class GreetingService implements IGreetingService {
createGreeting(name: string): string {
return `Hello, ${name}!`;
}
}
@injectable()
class Greeter implements IGreeter {
constructor(@inject(greetingServiceIdentifier) private greetingService: IGreetingService) {}
greet(name: string): string {
return this.greetingService.createGreeting(name);
}
}
// Set up dependency injection
const services = new ServiceCollection();
// Register services
services.addSingleton(greetingServiceIdentifier, GreetingService);
services.addScoped(greeterIdentifier, Greeter);
// Build service provider
const serviceProvider = services.build();
// Resolve and use a service
const greeter = serviceProvider.getService(greeterIdentifier);
console.log(greeter.greet('World')); // Outputs: Hello, World!
Service Lifetimes
netdi supports three service lifetimes:
Singleton
Singleton services are created once and shared by all consumers.
services.addSingleton(serviceIdentifier, Implementation);
Scoped
Scoped services are created once per scope. This is useful for services that should be shared within a request but not across requests.
services.addScoped(serviceIdentifier, Implementation);
// Create a scope
const scope = serviceProvider.createScope();
const scopedService = scope.serviceProvider.getService(serviceIdentifier);
// When done with the scope
scope.dispose();
Transient
Transient services are created each time they are requested.
services.addTransient(serviceIdentifier, Implementation);
Factory Registration
For complex service instantiation, you can use factory methods:
services.addSingleton(serviceIdentifier, provider => {
// Use the provider to get dependencies
const dependency = provider.getService(dependencyIdentifier);
// Create and configure your service instance
const instance = new MyService(dependency);
instance.configure();
return instance;
});
Creating Service Identifiers
Service identifiers help maintain type safety and prevent service conflicts:
// Create an identifier for a specific interface
const userServiceIdentifier = createServiceIdentifier<IUserService>();
// Then use it for registration and resolution
services.addSingleton(userServiceIdentifier, UserService);
const userService = serviceProvider.getService(userServiceIdentifier);
Decorators
@injectable()
Marks a class as injectable, allowing the container to create instances with dependencies:
@injectable()
class MyService {
constructor() {}
}
@inject()
Specifies a dependency for a parameter:
class MyService {
constructor(
@inject(loggerIdentifier) private logger: ILogger,
@inject(configIdentifier) private config: IConfig,
) {}
}
Advanced Topics
Service Disposal
Both service providers and scopes implement a dispose()
method:
// Dispose the root provider
serviceProvider.dispose();
// Dispose a scope
const scope = serviceProvider.createScope();
// ... use scope
scope.dispose();
Type Safety
netdi is designed to be fully type-safe. The getService()
method returns the exact type associated with the service identifier:
// TypeScript knows this is an IUserService
const userService = serviceProvider.getService(userServiceIdentifier);
License
MIT © Stevan Freeborn