Isolated resolver instances that can resolve dependencies and create class instances without modifying the parent container. ContainerResolver provides a scoped environment for dependency resolution with its own local bindings.
Isolated container resolver for dependency resolution without mutation capabilities.
/**
* Container resolver exposes the APIs to resolve bindings. You can think
* of resolver as an isolated container instance, with only the APIs
* to resolve bindings.
*/
class ContainerResolver<KnownBindings extends Record<any, any>> {
/** Checks if a binding exists in the resolver */
hasBinding<Binding extends keyof KnownBindings>(binding: Binding): boolean;
hasBinding(binding: BindingKey): boolean;
/** Checks if all specified bindings exist in the resolver */
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>>;
/** Resolves binding in context of a parent class for contextual bindings */
resolveFor<Binding>(
parent: unknown,
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]>>;
/** Registers a direct value in the resolver's local scope */
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;
}Usage Examples:
import { Container, inject } from "@adonisjs/fold";
const container = new Container();
// Register container-level bindings
container.singleton("logger", () => new Logger());
container.bindValue("app_name", "My App");
// Create isolated resolver
const resolver = container.createResolver();
// Resolver has access to container bindings
const logger = await resolver.make("logger");
const appName = await resolver.make("app_name");
// Resolver can bind its own values
resolver.bindValue("request_id", "req_123");
const requestId = await resolver.make("request_id");
// Container doesn't have access to resolver-specific bindings
try {
await container.make("request_id"); // Throws error
} catch (error) {
console.log("request_id not found in container");
}Create isolated resolver instances from the container.
/**
* Create a container resolver to resolve bindings, or make classes.
* Resolver values are isolated from the container.
*/
createResolver(): ContainerResolver<KnownBindings>;Usage Examples:
import { Container } from "@adonisjs/fold";
const container = new Container();
// Create multiple isolated resolvers
const userResolver = container.createResolver();
const adminResolver = container.createResolver();
// Each resolver can have its own context
userResolver.bindValue("user_role", "user");
adminResolver.bindValue("user_role", "admin");
// Resolvers are isolated
const userRole = await userResolver.make("user_role"); // "user"
const adminRole = await adminResolver.make("user_role"); // "admin"Register values that are specific to the resolver instance.
/**
* Registers a binding as a direct value in the resolver's local binding values.
* This method allows you to register pre-constructed instances or values that
* can be resolved later. The binding key must be a string, symbol, or class constructor.
*/
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;Usage Examples:
import { Container, inject } from "@adonisjs/fold";
@inject()
class UserService {
constructor(
private logger: Logger,
private currentUser: User
) {}
}
const container = new Container();
container.singleton("logger", () => new Logger());
// Create resolver for specific request context
const requestResolver = container.createResolver();
// Bind request-specific values
requestResolver.bindValue("current_user", new User({ id: 123, name: "Alice" }));
requestResolver.bindValue("request_id", "req_456");
// UserService gets request-specific current_user
const userService = await requestResolver.make(UserService);Resolve dependencies in the context of a specific parent class for contextual bindings.
/**
* Resolves binding in context of a parent. The method is the same as the "make" method,
* but takes a parent class constructor. This enables contextual binding resolution where
* different implementations can be injected based on the requesting parent class.
*/
resolveFor<Binding>(
parent: unknown,
binding: Binding,
runtimeValues?: any[],
createError?: ErrorCreator
): Promise<Make<Binding>>;Usage Examples:
import { Container, inject } from "@adonisjs/fold";
interface CacheContract {
get(key: string): Promise<any>;
set(key: string, value: any): Promise<void>;
}
class RedisCache implements CacheContract {
async get(key: string) { return "redis_value"; }
async set(key: string, value: any) {}
}
class MemoryCache implements CacheContract {
async get(key: string) { return "memory_value"; }
async set(key: string, value: any) {}
}
@inject()
class UserService {
constructor(private cache: CacheContract) {}
}
@inject()
class AdminService {
constructor(private cache: CacheContract) {}
}
const container = new Container();
// Set up contextual bindings
container
.when(UserService)
.give(CacheContract)
.use(() => new MemoryCache());
container
.when(AdminService)
.give(CacheContract)
.use(() => new RedisCache());
const resolver = container.createResolver();
// Resolve with specific parent context
const userCache = await resolver.resolveFor(UserService, CacheContract); // MemoryCache
const adminCache = await resolver.resolveFor(AdminService, CacheContract); // RedisCacheCall methods on objects with automatic dependency injection using the resolver.
/**
* Calls a method on an object by injecting its dependencies. The method dependencies
* are resolved in the same manner as class constructor dependencies, enabling
* dependency injection for method calls.
*/
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 ApiController {
@inject()
async getUsers(userService: UserService, logger: Logger) {
logger.info("Fetching users");
return userService.getAll();
}
@inject()
async createUser(userService: UserService, emailService: EmailService, userData: any) {
const user = await userService.create(userData);
await emailService.send(user.email, "Welcome!");
return user;
}
}
const container = new Container();
container.singleton("user_service", () => new UserService());
container.singleton("logger", () => new Logger());
container.singleton("email_service", () => new EmailService());
const resolver = container.createResolver();
const controller = new ApiController();
// Method injection with resolver
const users = await resolver.call(controller, "getUsers");
const newUser = await resolver.call(controller, "createUser", [{ name: "Alice" }]);Understanding the order of binding resolution in resolvers.
Resolution Order:
resolver.bindValue())Usage Examples:
import { Container } from "@adonisjs/fold";
const container = new Container();
// Container bindings
container.bindValue("service", "container_service");
container.bind("api_url", () => "container_api_url");
const resolver = container.createResolver();
// Resolver override takes precedence
resolver.bindValue("service", "resolver_service");
const service = await resolver.make("service"); // "resolver_service"
const apiUrl = await resolver.make("api_url"); // "container_api_url"Custom error handling for resolver operations.
Usage Examples:
import { Container } from "@adonisjs/fold";
import { RuntimeException } from "@poppinss/utils/exception";
const container = new Container();
const resolver = container.createResolver();
// Custom error creator
const createError = (message: string) => {
return new RuntimeException(`Resolver Error: ${message}`);
};
try {
const missing = await resolver.make("missing_binding", [], createError);
} catch (error) {
console.error(error.message); // "Resolver Error: Cannot resolve binding..."
}