Wireit API Reference
Complete API documentation for @vielzeug/wireit.
Table of Contents
Factory Functions
createToken()
Creates a typed token for dependency identification.
Signature
function createToken<T = unknown>(description?: string): Token<T>;Parameters
description?: string– Optional description for debugging
Returns
A typed symbol token
Example
const Logger = createToken<ILogger>('Logger');
const Config = createToken<AppConfig>('Config');
const Database = createToken<PrismaClient>('Database');
// Without description
const Cache = createToken<CacheService>();createContainer()
Creates a new dependency injection container.
Signature
function createContainer(options?: ContainerOptions): Container;Parameters
options?: ContainerOptions– Optional configuration
type ContainerOptions = {
parent?: Container;
allowOptional?: boolean;
};Returns
A new Container instance
Example
// Basic container
const container = createContainer();
// Container with parent
const child = createContainer({ parent: rootContainer });
// Container that returns undefined for missing tokens
const optional = createContainer({ allowOptional: true });createTestContainer()
Creates a test container with automatic cleanup.
Signature
function createTestContainer(base?: Container): {
container: Container;
dispose: () => void;
};Parameters
base?: Container– Optional base container to inherit from
Returns
Object with container and dispose function
Example
import { createTestContainer } from '@vielzeug/wireit';
describe('UserService', () => {
const { container, dispose } = createTestContainer();
afterEach(() => dispose());
it('should work', () => {
container.registerValue(Token, value);
// test...
});
});withMock()
Temporarily mock a dependency for testing.
Signature
function withMock<T, R>(container: Container, token: Token<T>, mock: T, fn: () => Promise<R> | R): Promise<R>;Parameters
container: Container– The container to modifytoken: Token<T>– Token to mockmock: T– Mock value or implementationfn: () => Promise<R> | R– Function to execute with mock
Returns
Promise resolving to the function result
Example
const mockDb = { query: vi.fn() };
await withMock(container, Database, mockDb, async () => {
const service = container.get(UserService);
await service.getUser('123');
expect(mockDb.query).toHaveBeenCalled();
});
// Original database is restoredContainer
Registration Methods
register()
Register a provider for a token.
Signature
register<T>(token: Token<T>, provider: Provider<T>): this;Parameters
token: Token<T>– Token to registerprovider: Provider<T>– Provider configuration
type Provider<T> =
| { useValue: T; lifetime?: Lifetime }
| { useClass: new (...args: any[]) => T; deps?: Token<any>[]; lifetime?: Lifetime }
| { useFactory: (...deps: any[]) => T | Promise<T>; deps?: Token<any>[]; lifetime?: Lifetime; async?: boolean };
type Lifetime = 'singleton' | 'transient' | 'scoped';Returns
The container instance (for chaining)
Example
// Value provider
container.register(Config, { useValue: config });
// Class provider
container.register(UserService, {
useClass: UserServiceImpl,
deps: [Database, Logger],
lifetime: 'singleton',
});
// Factory provider
container.register(Cache, {
useFactory: (config) => new RedisCache(config.redisUrl),
deps: [Config],
lifetime: 'singleton',
});
// Async factory
container.register(Database, {
useFactory: async (config) => {
const db = new PrismaClient();
await db.$connect();
return db;
},
deps: [Config],
async: true,
lifetime: 'singleton',
});registerValue()
Convenience method to register a value provider.
Signature
registerValue<T>(token: Token<T>, value: T, lifetime?: Lifetime): this;Parameters
token: Token<T>– Token to registervalue: T– Value to registerlifetime?: Lifetime– Optional lifetime (default:'singleton')
Example
container.registerValue(Config, { apiUrl: 'https://api.example.com' });
container.registerValue(RequestId, generateId(), 'transient');registerFactory()
Convenience method to register a factory provider.
Signature
registerFactory<T>(
token: Token<T>,
factory: (...deps: any[]) => T | Promise<T>,
deps?: Token<any>[],
options?: { lifetime?: Lifetime; async?: boolean }
): this;Parameters
token: Token<T>– Token to registerfactory: (...deps: any[]) => T | Promise<T>– Factory functiondeps?: Token<any>[]– Dependencies to injectoptions?: object– Optional configurationlifetime?: Lifetime– Lifetime (default:'transient')async?: boolean– Whether factory is async
Example
container.registerFactory(Logger, (config) => new ConsoleLogger(config.logLevel), [Config], { lifetime: 'singleton' });
container.registerFactory(
Database,
async (config) => {
const db = new PrismaClient();
await db.$connect();
return db;
},
[Config],
{ async: true, lifetime: 'singleton' },
);registerMany()
Register multiple providers at once.
Signature
registerMany(providers: Array<[Token<any>, Provider<any>]>): this;Parameters
providers: Array<[Token<any>, Provider<any>]>– Array of token/provider pairs
Example
container.registerMany([
[Config, { useValue: config }],
[Logger, { useClass: ConsoleLogger }],
[Database, { useClass: PrismaDatabase, deps: [Config] }],
]);Resolution Methods
get()
Resolve a dependency synchronously.
Signature
get<T>(token: Token<T>): T;Parameters
token: Token<T>– Token to resolve
Returns
The resolved instance
Throws
ProviderNotFoundError– Token not registeredAsyncProviderError– Provider is async, usegetAsync()CircularDependencyError– Circular dependency detected
Example
const logger = container.get(Logger);
const config = container.get(Config);getAsync()
Resolve a dependency asynchronously.
Signature
getAsync<T>(token: Token<T>): Promise<T>;Parameters
token: Token<T>– Token to resolve
Returns
Promise resolving to the instance
Example
const db = await container.getAsync(Database);
const service = await container.getAsync(UserService);getOptional()
Resolve a dependency, returning undefined if not found.
Signature
getOptional<T>(token: Token<T>): T | undefined;Parameters
token: Token<T>– Token to resolve
Returns
The instance or undefined
Example
const logger = container.getOptional(Logger);
if (logger) {
logger.info('Logger available');
}getOptionalAsync()
Async version of getOptional().
Signature
getOptionalAsync<T>(token: Token<T>): Promise<T | undefined>;Example
const cache = await container.getOptionalAsync(Cache);
if (cache) {
await cache.set('key', 'value');
}Management Methods
has()
Check if a token is registered.
Signature
has(token: Token<any>): boolean;Example
if (container.has(Logger)) {
const logger = container.get(Logger);
}alias()
Create an alias for a token.
Signature
alias<T>(source: Token<T>, alias: Token<T>): this;Parameters
source: Token<T>– Source tokenalias: Token<T>– Alias token
Example
const UserServiceImpl = createToken('UserServiceImpl');
const IUserService = createToken('IUserService');
container.register(UserServiceImpl, { useClass: UserService });
container.alias(UserServiceImpl, IUserService);
const service = container.get(IUserService); // Returns UserService instanceunregister()
Remove a registration.
Signature
unregister<T>(token: Token<T>): this;Example
container.unregister(Logger);
console.log(container.has(Logger)); // falseclear()
Clear all registrations.
Signature
clear(): void;Example
container.clear();debug()
Get debug information about registrations.
Signature
debug(): { tokens: string[]; aliases: Array<[string, string]> };Example
const debug = container.debug();
console.log(debug.tokens); // ['Config', 'Logger', 'Database']
console.log(debug.aliases); // [['ILogger', 'Logger']]Container Hierarchy
createChild()
Create a child container.
Signature
createChild(overrides?: Array<[Token<any>, Provider<any>]>): Container;Parameters
overrides?: Array<[Token<any>, Provider<any>]>– Optional initial registrations
Returns
New child container
Example
const child = container.createChild();
// With overrides
const child = container.createChild([
[RequestId, { useValue: generateId() }],
[User, { useValue: currentUser }],
]);runInScope()
Run a function in a scoped container with automatic cleanup.
Signature
runInScope<T>(
fn: (scope: Container) => Promise<T> | T,
overrides?: Array<[Token<any>, Provider<any>]>
): Promise<T>;Parameters
fn: (scope: Container) => Promise<T> | T– Function to executeoverrides?: Array<[Token<any>, Provider<any>]>– Optional initial registrations
Returns
Promise resolving to the function result
Example
await container.runInScope(
async (scope) => {
const handler = scope.get(RequestHandler);
return await handler.process(data);
},
[[RequestId, { useValue: generateId() }]],
);
// Scope is automatically cleaned upTesting Utilities
createTestContainer()
withMock()
Types
Token
type Token<T = unknown> = symbol & { __type?: T };Opaque type representing a typed dependency token.
Lifetime
type Lifetime = 'singleton' | 'transient' | 'scoped';Defines when and how often instances are created:
singleton– Created once, shared globallytransient– Created every timescoped– Created once per scope
Provider
type Provider<T> = ValueProvider<T> | ClassProvider<T> | FactoryProvider<T>;
type ValueProvider<T> = {
useValue: T;
lifetime?: Lifetime;
};
type ClassProvider<T> = {
useClass: new (...args: any[]) => T;
deps?: Token<any>[];
lifetime?: Lifetime;
};
type FactoryProvider<T> = {
useFactory: (...deps: any[]) => T | Promise<T>;
deps?: Token<any>[];
lifetime?: Lifetime;
async?: boolean;
};ContainerOptions
type ContainerOptions = {
parent?: Container;
allowOptional?: boolean;
};Options for creating a container:
parent– Parent container for hierarchical DIallowOptional– Return undefined for missing tokens instead of throwing
Errors
ProviderNotFoundError
Thrown when attempting to resolve a token that hasn't been registered.
class ProviderNotFoundError extends Error {
constructor(token: Token<any>);
}Example:
try {
const service = container.get(UnknownToken);
} catch (error) {
if (error instanceof ProviderNotFoundError) {
console.error('Provider not found:', error.message);
}
}AsyncProviderError
Thrown when using get() with an async provider.
class AsyncProviderError extends Error {
constructor(token: Token<any>);
}Example:
container.registerFactory(DB, async () => db, [], { async: true });
try {
const db = container.get(DB); // Error!
} catch (error) {
if (error instanceof AsyncProviderError) {
const db = await container.getAsync(DB); // Correct
}
}CircularDependencyError
Thrown when a circular dependency is detected.
class CircularDependencyError extends Error {
constructor(path: Token<any>[]);
}Example:
container.register(ServiceA, { useClass: A, deps: [ServiceB] });
container.register(ServiceB, { useClass: B, deps: [ServiceA] });
try {
const a = container.get(ServiceA);
} catch (error) {
if (error instanceof CircularDependencyError) {
console.error('Circular dependency detected:', error.message);
// Circular dependency detected: ServiceA → ServiceB → ServiceA
}
}Best Practices
Token Organization
// tokens.ts – Centralize token definitions
export const Config = createToken<AppConfig>('Config');
export const Logger = createToken<ILogger>('Logger');
export const Database = createToken<PrismaClient>('Database');
export const UserService = createToken<IUserService>('UserService');Type Safety
// ✅ Use interfaces for flexibility
interface ILogger {
info(message: string): void;
error(message: string): void;
}
const Logger = createToken<ILogger>('Logger');
// ❌ Avoid concrete types
const Logger = createToken<ConsoleLogger>('Logger');Lifetime Selection
// ✅ Singleton for expensive resources
container.register(Database, {
useClass: PrismaDatabase,
lifetime: 'singleton',
});
// ✅ Transient for lightweight objects
container.registerFactory(RequestId, () => generateId(), [], {
lifetime: 'transient',
});
// ✅ Scoped for request-specific data
container.register(RequestContext, {
useClass: Context,
lifetime: 'scoped',
});Error Handling
// ✅ Use optional resolution when appropriate
const logger = container.getOptional(Logger);
if (logger) {
logger.info('Starting...');
}
// ✅ Handle specific errors
try {
const service = container.get(Service);
} catch (error) {
if (error instanceof ProviderNotFoundError) {
// Handle missing provider
} else if (error instanceof CircularDependencyError) {
// Handle circular dependency
}
}