A lightweight, lazy, and typesafe dependency injection library for TypeScript.
- Lazy Initialization: Services are only created when they are actually needed.
- Type-Safe: Full TypeScript support with typed keys and dependency resolution.
- Circular Dependency Detection: Validates your dependency graph at module creation.
- Flexible Scoping: Support for singletons, transient (one-shot) services, and custom scopes.
- Runtime Selection: Dynamically choose between multiple implementations of the same interface.
- Async Support: Native support for asynchronous service initialization.
- Visualization: Built-in support for generating Mermaid and DOT diagrams of your dependency graph.
npm install lazy-diService keys are typed tokens that identify your services. They ensure type safety when injecting and retrieving services.
import { ServiceKey } from 'lazy-di';
interface Database {
query: (sql: string) => Promise<any>;
}
export const DatabaseKey = new ServiceKey<Database>('Database');
interface UserService {
getUser: (id: string) => Promise<any>;
}
export const UserServiceKey = new ServiceKey<UserService>('UserService');Factories define how services are created and what they depend on. lazy-di supports both singleton (created once) and one-shot (created every time) services.
import { ServiceFactory } from 'lazy-di';
import { DatabaseKey, UserServiceKey } from './keys';
const databaseFactory = ServiceFactory.singleton({
provides: DatabaseKey,
initialize: () => {
console.log('Initializing Database...');
return {
query: async (sql) => ({ id: '1', name: 'John Doe' }),
};
},
});
const userServiceFactory = ServiceFactory.singleton({
provides: UserServiceKey,
dependsOn: [DatabaseKey],
initialize: (db) => {
// db is automatically typed as Database
return {
getUser: (id) => db.query(`SELECT * FROM users WHERE id = ${id}`),
};
},
});A ServiceModule aggregates factories and manages their lifecycle.
import { ServiceModule } from 'lazy-di';
const module = ServiceModule.from([
databaseFactory,
userServiceFactory
]);
// At this point, no services have been initialized.
// This will initialize Database and then UserService lazily.
const userService = await module.get(UserServiceKey);
const user = await userService.getUser('1');A unique identifier for a service of type T.
const MyKey = new ServiceKey<MyInterface>('MyService');Creates a factory for a service that is instantiated only once.
provides: TheServiceKeythis factory satisfies.dependsOn: (Optional) An array ofServiceKeys this service depends on.initialize: A function that creates the service instance. It receives the resolved dependencies as arguments. Can return a Promise.dispose: (Optional) A function called when the service is disposed.scope: (Optional) AServiceScopefor grouping services.
Creates a factory for a service that is instantiated every time it is requested.
Creates a module from an array of factories or other ServiceModule instances. It automatically detects circular dependencies and missing dependencies.
Retrieves a service instance. Returns a Promise<T>.
Disposes of services. If a scope is provided, only services in that scope are disposed.
Useful for choosing between multiple implementations of the same interface at runtime.
const LoggerSelectorKey = new ServiceSelectorKey<Logger>([
ConsoleLoggerKey,
FileLoggerKey,
]);
const AppFactory = ServiceFactory.singleton({
provides: AppKey,
dependsOn: [LoggerSelectorKey],
initialize: (loggerSelector) => {
return {
run: async (useFile: boolean) => {
const logger = await loggerSelector.get(
useFile ? FileLoggerKey : ConsoleLoggerKey
);
logger.log('Running...');
}
};
}
});Used to group services for collective disposal.
const MyScope = new ServiceScope('MyScope');Visualize your dependency graph using Mermaid or DOT format.
import { printMermaidGraph, printDotGraph } from 'lazy-di';
// Outputs a Mermaid diagram string
printMermaidGraph(module);
// Outputs a DOT diagram string
printDotGraph(module);ServiceModuleInitError: Thrown duringServiceModule.from()if the dependency graph is invalid (circular or missing dependencies).ServiceFactoryNotFoundError: Thrown bymodule.get()if a requested key is not registered.
MIT