
Product
Introducing Repository Access Permissions and Custom Roles
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.
@fluojs/di
Advanced tools
Minimal token-based dependency injection container powering every Fluo application.
English 한국어
Minimal token-based dependency injection container powering every fluo application.
npm install @fluojs/di
Use this package when you need to:
The container resolves tokens into instances based on their registered providers.
import { Container } from '@fluojs/di';
import { Inject, Scope } from '@fluojs/core';
class Logger {
log(msg: string) { console.log(msg); }
}
@Inject(Logger)
@Scope('singleton')
class UserService {
constructor(private logger: Logger) {}
async getStatus() {
this.logger.log('Checking status...');
return { status: 'active' };
}
}
const container = new Container();
container.register(Logger, UserService);
const service = await container.resolve(UserService);
const result = await service.getStatus();
fluo DI supports four provider shapes:
container.register(MyService) or { provide: MyToken, useClass: MyService }.{ provide: 'API_URL', useValue: 'https://api.example.com' }.{ provide: 'ASYNC_CONFIG', useFactory: async (db) => await db.load(), inject: [Database] }.{ provide: ILogger, useExisting: PinoLogger } allows mapping one token to another existing provider.createRequestScope() call.During disposal, the root container first tears down live request-scope children and then continues with root-owned singleton cleanup even if one or more child disposals fail. When multiple child/root disposals fail, dispose() reports an AggregateError so callers can inspect every shutdown failure without losing cleanup progress.
Use override(...providers) when a test or request-local boundary needs to replace existing registrations deliberately. Overrides replace the current provider set for each token, invalidate cached instances in the current container and already-materialized request-scope descendants, and dispose stale instances immediately. Multi-provider overrides replace the full multi-provider set for that token, so pass every replacement provider together; mixing single and multi replacements for the same token in one override call is rejected as ambiguous.
Isolated containers can be created to handle per-request state without polluting the root container.
const requestContainer = container.createRequestScope();
const scopedService = await requestContainer.resolve(RequestScopedService);
Request-scope containers may resolve providers from their parent chain, but request-owned registrations must not introduce new singleton providers. Register singleton providers on the root container before creating request scopes. If a request scope needs local additions, declare them with scope: 'request'/Scope.REQUEST or use override() for an explicit request-local replacement. The same rule applies to multi providers: default-scope multi providers belong on the root container, while request-local multi providers must opt into request scope or be replaced through override().
Provider objects are validated at registration time: every object provider must include a non-null provide token and exactly one strategy (useClass, useValue, useFactory, or useExisting). Invalid provider shapes throw InvalidProviderError before they can affect the container graph.
The container automatically detects circular dependencies and throws a CircularDependencyError to prevent infinite loops. This includes direct (A→A), two-node (A→B→A), and deep (A→B→C→A) cycles.
Use forwardRef() when a token is referenced before its declaration. It defers token lookup for declaration-order issues, but it does not make true constructor cycles resolvable; those cycles are still rejected with CircularDependencyError.
import { forwardRef } from '@fluojs/di';
import { Inject } from '@fluojs/core';
@Inject(forwardRef(() => ServiceB))
class ServiceA {
constructor(private readonly serviceB: ServiceB) {}
}
class ServiceB {
getStatus() {
return 'ready';
}
}
forwardRef(...) and optional(...) are token wrappers used inside the class-level @Inject(...) token list or provider-level inject arrays. They are not decorators and do not attach to constructor parameters.
import { optional } from '@fluojs/di';
import { Inject } from '@fluojs/core';
@Inject(optional(AuditLogger))
class ServiceWithOptionalLogger {
constructor(private readonly auditLogger: AuditLogger | undefined) {}
}
You can easily override providers in the container to use mocks or stubs during unit testing by using useValue.
import { Container } from '@fluojs/di';
const container = new Container();
const mockDb = { query: vi.fn() };
// Override the real Database class with a mock value
container.register({
provide: Database,
useValue: mockDb
});
const service = await container.resolve(DataService);
// service uses mockDb instead of the real Database instance
Thrown when the container detects a cycle in the dependency graph. Check your constructor injections and remove the cycle by extracting shared state, introducing a mediator, or changing the lifetime boundary. forwardRef() only defers token lookup for declaration-order issues; it does not break true constructor cycles.
Ensure all required providers are registered in the container. If you use createRequestScope(), the child container can resolve tokens from the parent, but not vice versa.
| Export | Description |
|---|---|
Container | The main DI container class. |
register(...providers) | Registers one or more providers. |
override(...providers) | Replaces existing providers, invalidates cached instances, and disposes stale instances. |
resolve<T>(token) | Asynchronously resolves a token to an instance. |
inspectResolutionState() | Exposes the supported framework-owned container introspection seam for testing/tooling helpers that must preserve cache ownership. Prefer has(...) and resolve(...) for application code. |
createRequestScope() | Creates a child container for request-scoped dependencies. |
has(token) | Checks if a token is registered in the container or its parents. |
hasRequestScopedDependency(token) | Checks whether resolving a token may require a request-scope container because its provider graph contains request-scoped dependencies or is cyclic. |
dispose() | Disposes request children and root-owned singleton instances. |
forwardRef(fn) | Returns a token wrapper that defers lookup for declaration-order issues; it does not make constructor dependency cycles resolvable. |
isForwardRef(value) | Type guard for values produced by forwardRef(...); useful when integrating custom provider tooling with DI token wrappers. |
optional(token) | Returns a token wrapper that marks one dependency as optional; missing optional dependencies resolve to undefined. |
isOptionalToken(value) | Type guard for values produced by optional(...); useful when inspecting provider-level inject arrays. |
Scope | Exposes DEFAULT, REQUEST, and TRANSIENT scope constants. |
| Provider types | Provider, ClassProvider, FactoryProvider, ValueProvider, and ExistingProvider describe the public registration shapes accepted by register(...) and override(...). |
| Token wrapper types | ForwardRefFn and OptionalToken describe the wrapper values returned by forwardRef(...) and optional(...). |
| Container helper types | ClassType, Disposable, and RequestScopeContainer support typed provider declarations, teardown hooks, and request-scope helper boundaries. |
ContainerResolutionState | Public introspection record returned by inspectResolutionState() for framework testing/tooling integrations. |
NormalizedProvider | Compatibility-only public type for the container's validated provider record shape. Prefer authoring providers with Provider or the specific provider interfaces; the container owns normalized record construction. |
DiErrorContext | Structured context attached to DI errors so logs and tests can inspect tokens, scopes, modules, dependency chains, and hints. |
| Error classes | InvalidProviderError, ContainerResolutionError, RequestScopeResolutionError, ScopeMismatchError, CircularDependencyError, DuplicateProviderError. |
Resolving a multi-provider token returns an array of resolved values in registration order.
@fluojs/core: Defines the @Inject() and @Scope() decorators used to annotate classes.@fluojs/runtime: Handles automatic registration of providers during application bootstrap.@fluojs/http: Creates a request scope for every incoming HTTP request.packages/di/src/container.tspackages/di/src/container.test.tsexamples/minimal/src/app.tsFAQs
Minimal token-based dependency injection container powering every Fluo application.
We found that @fluojs/di demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.

Product
Socket MCP now lets AI assistants review org alerts, investigate threats using the Socket threat feed, and inspect package files in addition to dependency scoring.

Product
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.