Core IoC container functionality for registering dependencies, creating instances, and managing the dependency lifecycle. The Container class provides the main API for dependency injection in AdonisJS Fold.
Main IoC container providing dependency injection, binding registration, and instance resolution.
/**
* Main IoC container providing dependency injection, binding registration, and instance resolution
* @template KnownBindings - Type definition for known bindings for type safety
*/
class Container<KnownBindings extends Record<any, any>> {
/** Creates a new Container instance */
constructor(options?: ContainerOptions);
/** Sets an event emitter for container events */
useEmitter(emitter: Exclude<ContainerOptions['emitter'], undefined>): Container<KnownBindings>;
/** Creates an isolated resolver instance */
createResolver(): ContainerResolver<KnownBindings>;
/** Checks if a binding exists in the container */
hasBinding<Binding extends keyof KnownBindings>(binding: Binding): boolean;
hasBinding(binding: BindingKey): boolean;
/** Checks if all specified bindings exist in the container */
hasAllBindings<Binding extends keyof KnownBindings>(bindings: Binding[]): boolean;
hasAllBindings(bindings: BindingKey[]): boolean;
/** Resolves a binding or constructs a class instance */
make<Binding extends keyof KnownBindings>(
binding: Binding,
runtimeValues?: any[],
createError?: ErrorCreator
): Promise<Binding extends string | symbol ? KnownBindings[Binding] : Make<Binding>>;
make<Binding>(
binding: Binding,
runtimeValues?: any[],
createError?: ErrorCreator
): Promise<Make<Binding>>;
/** Calls a method on an object by injecting its dependencies */
call<Value extends Record<any, any>, Method extends ExtractFunctions<Value>>(
value: Value,
method: Method,
runtimeValues?: any[],
createError?: ErrorCreator
): Promise<ReturnType<Value[Method]>>;
}Usage Examples:
import { Container } from "@adonisjs/fold";
// Create container with type safety
interface MyBindings {
logger: Logger;
database: Database;
}
const container = new Container<MyBindings>();
// Basic container usage
const logger = await container.make("logger");
const userService = await container.make(UserService);
// Check if bindings exist
if (container.hasBinding("logger")) {
const logger = await container.make("logger");
}
// Check multiple bindings
if (container.hasAllBindings(["logger", "database"])) {
// Both bindings are available
}Configure the container with optional event emission support.
interface ContainerOptions {
/** Event emitter for container events */
emitter?: {
emit(event: string | symbol, ...values: any[]): any;
};
}Usage Examples:
import { Container } from "@adonisjs/fold";
import { EventEmitter } from "events";
const emitter = new EventEmitter();
const container = new Container({ emitter });
// Or set emitter later
container.useEmitter(emitter);
// Listen for resolution events
emitter.on('container_binding:resolved', (data) => {
console.log(`Resolved binding: ${data.binding}`);
});Call methods on objects with automatic dependency injection.
/**
* Calls a method on an object by injecting its dependencies
* @param value - The object containing the method
* @param method - The method name to call
* @param runtimeValues - Optional runtime values for method parameters
* @param createError - Optional error creator function
* @returns Promise resolving to the method's return value
*/
call<Value extends Record<any, any>, Method extends ExtractFunctions<Value>>(
value: Value,
method: Method,
runtimeValues?: any[],
createError?: ErrorCreator
): Promise<ReturnType<Value[Method]>>;Usage Examples:
import { Container, inject } from "@adonisjs/fold";
class UserController {
@inject()
async index(logger: Logger, request: Request) {
logger.info("Fetching users");
return request.users();
}
}
const container = new Container();
const controller = new UserController();
// Call method with dependency injection
const result = await container.call(controller, "index");Customize error creation for better debugging and error tracking.
type ErrorCreator = (message: string) => Exception;Usage Examples:
import { Container } from "@adonisjs/fold";
import { RuntimeException } from "@poppinss/utils/exception";
const container = new Container();
// Custom error creator for better stack traces
const createError = (message: string) => {
const error = new RuntimeException(message);
error.stack = new Error().stack;
return error;
};
// Use custom error creator
const userService = await container.make(UserService, [], createError);Register dependencies in the container using various binding methods.
/**
* Register an alias for a binding. The value can be a reference
* to an existing binding or to a class constructor.
*/
alias<Alias extends keyof KnownBindings>(
alias: Alias extends string | symbol ? Alias : never,
value: keyof KnownBindings | AbstractConstructor<any>
): void;
/**
* Register a binding inside the container with a factory function.
* The factory function is invoked each time the binding is resolved.
*/
bind<Binding extends keyof KnownBindings>(
binding: Binding extends string | symbol ? Binding : never,
resolver: BindingResolver<KnownBindings, KnownBindings[Binding]>
): void;
bind<Binding extends AbstractConstructor<any>>(
binding: Binding,
resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
): void;
/**
* Register a binding as a direct value
*/
bindValue<Binding extends keyof KnownBindings>(
binding: Binding extends string | symbol ? Binding : never,
value: KnownBindings[Binding]
): void;
bindValue<Binding extends AbstractConstructor<any>>(
binding: Binding,
value: InstanceType<Binding>
): void;
/**
* Register a binding as a singleton. The factory function is invoked
* only once and the result is cached.
*/
singleton<Binding extends keyof KnownBindings>(
binding: Binding extends string | symbol ? Binding : never,
resolver: BindingResolver<KnownBindings, KnownBindings[Binding]>
): void;
singleton<Binding extends AbstractConstructor<any>>(
binding: Binding,
resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
): void;Usage Examples:
import { Container } from "@adonisjs/fold";
const container = new Container();
// Alias registration
container.singleton("primary_logger", () => new Logger());
container.alias("logger", "primary_logger");
// Factory binding - new instance each time
container.bind("request_id", () => Math.random().toString());
// Direct value binding
container.bindValue("app_name", "My Application");
container.bindValue(Logger, new Logger());
// Singleton binding - cached instance
container.singleton("database", (resolver) => {
const config = resolver.make("db_config");
return new Database(config);
});Replace dependencies with fakes or mocks during testing.
/**
* Define a fake implementation for a class constructor.
* Fakes have the highest priority when resolving dependencies.
*/
swap<Binding extends AbstractConstructor<any>>(
binding: Binding,
resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
): void;
/**
* Restores a binding by removing its swap
*/
restore(binding: AbstractConstructor<any>): void;
/**
* Restores multiple or all bindings by removing their swaps
*/
restoreAll(bindings?: AbstractConstructor<any>[]): void;Usage Examples:
import { Container, inject } from "@adonisjs/fold";
@inject()
class UserService {
constructor(private emailService: EmailService) {}
}
const container = new Container();
// Swap with mock implementation
container.swap(EmailService, () => ({
send: jest.fn().mockResolvedValue(undefined)
}));
const userService = await container.make(UserService);
// userService uses mocked EmailService
// Restore original implementation
container.restore(EmailService);
// Or restore all swaps
container.restoreAll();Register hooks that execute after a binding has been resolved.
/**
* Defines hooks to be executed after a binding has been resolved.
* Hooks are executed for bindings, singletons, and class constructors.
*/
resolving<Binding extends keyof KnownBindings>(
binding: Binding,
callback: HookCallback<KnownBindings, KnownBindings[Binding]>
): void;
resolving<Binding extends AbstractConstructor<any>>(
binding: Binding,
callback: HookCallback<KnownBindings, InstanceType<Binding>>
): void;Usage Examples:
import { Container } from "@adonisjs/fold";
const container = new Container();
// Hook for specific binding
container.resolving("logger", (logger, resolver) => {
console.log("Logger resolved");
logger.setLevel("debug");
});
// Hook for class constructor
container.resolving(UserService, async (userService, resolver) => {
await userService.initialize();
});Register different implementations based on the requesting class context.
/**
* Creates a contextual builder to define contextual bindings
*/
when(parent: Constructor<any>): ContextBindingsBuilder<KnownBindings, AbstractConstructor<any>>;
/**
* Adds a contextual binding for a given class constructor
*/
contextualBinding<Binding extends AbstractConstructor<any>>(
parent: Constructor<any>,
binding: Binding,
resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
): void;Usage Examples:
import { Container, inject } from "@adonisjs/fold";
interface HashContract {
hash(value: string): Promise<string>;
}
@inject()
class UserService {
constructor(private hasher: HashContract) {}
}
@inject()
class AdminService {
constructor(private hasher: HashContract) {}
}
const container = new Container();
// Contextual binding using fluent API
container
.when(UserService)
.give(HashContract)
.use(() => new BcryptHash());
// Direct contextual binding
container.contextualBinding(
AdminService,
HashContract,
() => new Argon2Hash()
);
// Different implementations based on requesting class
const userService = await container.make(UserService); // Gets BcryptHash
const adminService = await container.make(AdminService); // Gets Argon2Hash