Multiple binding types for registering dependencies including direct values, factory functions, singletons, and contextual bindings. The binding system allows flexible dependency registration patterns to suit different use cases.
Register pre-constructed instances or primitive values directly in the container.
/**
* Registers a binding as a direct value
* @param binding - The binding key (string, symbol, or class constructor)
* @param value - The actual value to bind
*/
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 } from "@adonisjs/fold";
const container = new Container();
// Bind primitive values
container.bindValue("app_name", "My Application");
container.bindValue("port", 3000);
container.bindValue("debug", true);
// Bind pre-constructed instances
const logger = new Logger({ level: "info" });
container.bindValue("logger", logger);
container.bindValue(Logger, logger);
// Bind configuration objects
container.bindValue("database_config", {
host: "localhost",
port: 5432,
database: "myapp"
});
// Use the bound values
const appName = await container.make("app_name");
const dbConfig = await container.make("database_config");Register factory functions that create instances each time they're resolved.
/**
* Registers a binding with a factory function
* @param binding - The binding key (string, symbol, or class constructor)
* @param resolver - Factory function to construct the dependency
*/
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;Usage Examples:
import { Container } from "@adonisjs/fold";
const container = new Container();
// Simple factory function
container.bind("random_id", () => {
return Math.random().toString(36).substring(7);
});
// Factory with dependencies
container.bind("database", (resolver) => {
const config = resolver.make("database_config");
return new Database(config);
});
// Async factory function
container.bind("api_client", async (resolver) => {
const token = await resolver.make("auth_token");
return new ApiClient({ token });
});
// Factory with runtime values
container.bind("user_service", (resolver, runtimeValues) => {
const database = resolver.make("database");
const logger = resolver.make("logger");
const options = runtimeValues?.[0] || {};
return new UserService(database, logger, options);
});
// Use the bindings - new instance each time
const id1 = await container.make("random_id");
const id2 = await container.make("random_id"); // Different ID
const userService = await container.make("user_service", [{ cache: true }]);Register factory functions that create instances only once and cache the result.
/**
* Registers a binding as a singleton
* @param binding - The binding key (string, symbol, or class constructor)
* @param resolver - Factory function to construct the dependency (called only once)
*/
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();
// Singleton database connection
container.singleton("database", (resolver) => {
const config = resolver.make("database_config");
return new Database(config);
});
// Singleton logger
container.singleton("logger", () => {
return new Logger({ level: "info" });
});
// Expensive singleton with async initialization
container.singleton("cache", async (resolver) => {
const config = await resolver.make("cache_config");
const cache = new RedisCache(config);
await cache.connect();
return cache;
});
// Use singletons - same instance returned
const db1 = await container.make("database");
const db2 = await container.make("database"); // Same instance
console.log(db1 === db2); // trueCreate aliases that point to existing bindings or class constructors.
/**
* Registers an alias for a binding
* @param alias - The alias key (must be string or symbol)
* @param value - Reference to existing binding or class constructor
*/
alias<Alias extends keyof KnownBindings>(
alias: Alias extends string | symbol ? Alias : never,
value: keyof KnownBindings | AbstractConstructor<any>
): void;Usage Examples:
import { Container } from "@adonisjs/fold";
const container = new Container();
// Register original bindings
container.singleton("primary_database", () => new Database("primary"));
container.bind(Logger, () => new Logger());
// Create aliases
container.alias("db", "primary_database");
container.alias("log", Logger);
// Use aliases
const database = await container.make("db"); // Same as "primary_database"
const logger = await container.make("log"); // Same as LoggerRegister different implementations based on the requesting class context.
/**
* Creates a contextual builder to define contextual bindings
* @param parent - The parent class constructor
* @returns ContextBindingsBuilder instance for fluent API
*/
when(parent: Constructor<any>): ContextBindingsBuilder<KnownBindings, AbstractConstructor<any>>;
/**
* Adds a contextual binding for a given class constructor
* @param parent - The parent class constructor
* @param binding - The dependency class constructor
* @param resolver - Factory function to resolve the dependency in this context
*/
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>;
}
class Argon2Hash implements HashContract {
async hash(value: string) { return "argon2_hash"; }
}
class BcryptHash implements HashContract {
async hash(value: string) { return "bcrypt_hash"; }
}
@inject()
class UserService {
constructor(private hasher: HashContract) {}
}
@inject()
class AdminService {
constructor(private hasher: HashContract) {}
}
const container = new Container();
// Contextual bindings using fluent API
container
.when(UserService)
.give(HashContract)
.use(() => new BcryptHash());
container
.when(AdminService)
.give(HashContract)
.use(() => new Argon2Hash());
// Or using direct method
container.contextualBinding(
UserService,
HashContract,
() => new BcryptHash()
);
// Different implementations injected based on context
const userService = await container.make(UserService); // Gets BcryptHash
const adminService = await container.make(AdminService); // Gets Argon2HashFluent API for creating contextual bindings with a readable syntax.
class ContextBindingsBuilder<KnownBindings extends Record<any, any>, Binding> {
/**
* Specifies dependency to provide in the contextual binding
* @param binding - The dependency class constructor to provide
* @returns Builder instance for method chaining
*/
give<GiveBinding extends AbstractConstructor<any>>(
binding: GiveBinding
): ContextBindingsBuilder<KnownBindings, GiveBinding>;
/**
* Specifies resolver function for the contextual binding
* @param resolver - Factory function to resolve the dependency
*/
use(resolver: BindingResolver<KnownBindings, InstanceType<Binding>>): void;
}Usage Examples:
import { Container, inject } from "@adonisjs/fold";
interface StorageContract {
store(file: Buffer): Promise<string>;
}
class LocalStorage implements StorageContract {
async store(file: Buffer) { return "local_path"; }
}
class S3Storage implements StorageContract {
async store(file: Buffer) { return "s3_url"; }
}
@inject()
class UserController {
constructor(private storage: StorageContract) {}
}
@inject()
class AdminController {
constructor(private storage: StorageContract) {}
}
const container = new Container();
// Fluent contextual binding
container
.when(UserController)
.give(StorageContract)
.use((resolver) => {
const config = resolver.make("storage_config");
return new LocalStorage(config);
});
container
.when(AdminController)
.give(StorageContract)
.use(() => new S3Storage());
// Different storage implementations based on controller context
const userController = await container.make(UserController); // Gets LocalStorage
const adminController = await container.make(AdminController); // Gets S3StorageThe factory function signature used for all binding types.
/**
* Shape of the binding resolver function
*/
type BindingResolver<KnownBindings extends Record<any, any>, Value> = (
resolver: ContainerResolver<KnownBindings>,
runtimeValues?: any[]
) => Value | Promise<Value>;Usage Examples:
import { Container, BindingResolver } from "@adonisjs/fold";
const container = new Container();
// Synchronous resolver
const syncResolver: BindingResolver<any, Logger> = (resolver) => {
return new Logger();
};
// Asynchronous resolver
const asyncResolver: BindingResolver<any, Database> = async (resolver) => {
const config = await resolver.make("config");
const db = new Database(config);
await db.connect();
return db;
};
// Resolver with runtime values
const parameterizedResolver: BindingResolver<any, ApiClient> = (resolver, runtimeValues) => {
const baseUrl = resolver.make("api_base_url");
const options = runtimeValues?.[0] || {};
return new ApiClient(baseUrl, options);
};
container.bind("logger", syncResolver);
container.singleton("database", asyncResolver);
container.bind("api_client", parameterizedResolver);