@filipgorny/di
v0.0.6
Published
Lightweight dependency injection container
Maintainers
Readme
@filipgorny/di
Lightweight dependency injection container for managing application dependencies.
Features
- 🎯 Simple API - Just
register()andget() - ⚛️ React Hook -
useContainer()for React components - 📦 Type-safe - Full TypeScript support
- 🧪 Testable - Create isolated containers for testing
- 🪶 Lightweight - Minimal dependencies
Installation
pnpm add @filipgorny/diUsage
import { createContainer } from "@filipgorny/di";
import { Inject } from "@filipgorny/di";
// services/logger.ts
export class Logger {
log(message: string) {
console.log(message);
}
}
// services/user-service.ts
export class UserService {
constructor(@Inject() private logger: Logger) {}
doSomething() {
this.logger.log("Doing something");
}
}
// app.ts
export class App {
constructor(@Inject() private userService: UserService) {}
run() {
this.userService.doSomething();
}
}
// container.ts
const container = createContainer();
// Register dependencies
container.register("logger", Logger);
container.register("userService", UserService);
container.register("app", App);
// Get the app instance
const app = container.get<App>("app");
app.run();Register Instances Directly
const container = createContainer();
// Create instance with custom configuration
const logger = new Logger({ level: "debug", format: "json" });
// Register the instance
container.registerInstance("logger", logger);
// Get the same instance
const sameLogger = container.get<Logger>("logger");React Hook
import { ContainerProvider, useContainer } from "@filipgorny/di";
const container = createContainer();
// ... register dependencies
function App() {
return (
<ContainerProvider container={container}>
<MyComponent />
</ContainerProvider>
);
}
function MyComponent() {
const logger = useContainer("logger");
const userService = useContainer(UserService);
// Instances are memoized per render
return <div>...</div>;
}Check if Registered
const container = createContainer();
if (container.has("logger")) {
const logger = container.get<Logger>("logger");
}Clear Dependencies
const container = createContainer();
// Clear specific dependency
container.clear("logger");
// Clear all
container.clearAll();List Registered Dependencies
const container = createContainer();
const deps = container.getRegisteredNames();
console.log(deps); // ['logger', 'userService', 'db']API
register<T>(name: string, classType: ClassType<T>): void
Register a class with the container.
name- Unique identifierclassType- The class to register
registerInstance<T>(name: string, instance: T): void
Register a pre-created instance as a singleton.
name- Unique identifierinstance- The instance to register
get<T>(name: string): T
Get an instance of a registered dependency.
name- The name of the dependency- Returns the dependency instance (throws if not registered)
has(name: string): boolean
Check if a dependency is registered.
clear(name: string): void
Remove a specific dependency from the container.
clearAll(): void
Remove all dependencies from the container.
getRegisteredNames(): string[]
Get a list of all registered dependency names.
ContainerProvider
React context provider for the container.
useContainer<T>(nameOrClass: string | ClassType<T>): T
React hook to get dependencies from the container. Must be used within a ContainerProvider. Instances are memoized to prevent unnecessary re-creation.
Patterns
Constructor Injection with Decorators
import { Inject } from "@filipgorny/di";
// services/user-service.ts
export class UserService {
constructor(
@Inject() private database: Database,
@Inject() private logger: Logger,
) {}
async getUser(id: string) {
this.logger.info(`Fetching user ${id}`);
return this.database.findUser(id);
}
}Best Practices
- Register early - Set up your container at application startup
- Composition root - Use the container only in the composition root, never access it globally
- Named constants - Use constants for dependency names to avoid typos
- Type safety - Always use generics when calling
get<T>() - Use constructor injection - Prefer @Inject() decorator over manual container.get()
- Avoid service locator - Never call container.get() inside classes
Example: Dependency Names
// constants/di-names.ts
export const DI = {
LOGGER: "logger",
DATABASE: "database",
USER_SERVICE: "userService",
LLM_PROVIDER: "llmProvider",
} as const;
// Usage
import { DI } from "./constants/di-names";
import { createContainer } from "@filipgorny/di";
const container = createContainer();
container.register(DI.LOGGER, Logger);
const logger = container.get<Logger>(DI.LOGGER);Comparison with Other DI Libraries
| Feature | @filipgorny/di | InversifyJS | NestJS | | -------------- | --------------------- | ----------- | --------------- | | Learning curve | Low | Medium | High | | React hooks | Yes | No | No | | Decorators | Yes | Yes | Yes | | Size | ~2KB | ~50KB | Full framework | | Best for | Learning, simple apps | Large apps | Enterprise apps |
