Basic Usage
Learn the fundamental concepts and injection patterns in NeoSyringe.
Container Configuration
All configuration is done through defineBuilderConfig:
import { defineBuilderConfig } from '@djodjonx/neosyringe';
export const container = defineBuilderConfig({
name: 'MyContainer', // Optional: container name for debugging
injections: [ // Required: list of injections
// ... your injections
],
extends: [], // Optional: inherit from partials
useContainer: undefined // Optional: parent container
});Injection Types
Class Token (Autowire)
The simplest form - register a class and let NeoSyringe resolve its dependencies:
class UserRepository {
findAll() { return []; }
}
class UserService {
constructor(private repo: UserRepository) {}
}
export const container = defineBuilderConfig({
injections: [
{ token: UserRepository },
{ token: UserService } // Dependencies auto-resolved
]
});Interface Token
Use useInterface<T>() to bind interfaces to implementations:
import { useInterface } from '@djodjonx/neosyringe';
interface ILogger {
log(msg: string): void;
}
class ConsoleLogger implements ILogger {
log(msg: string) { console.log(msg); }
}
class FileLogger implements ILogger {
log(msg: string) { /* write to file */ }
}
export const container = defineBuilderConfig({
injections: [
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
]
});
// Resolve by interface
const logger = container.resolve(useInterface<ILogger>());Automatic ID Generation
useInterface<ILogger>() generates a unique string ID at compile-time. You don't need to manage Symbols manually!
Explicit Provider
Override the default implementation:
class UserService {
// ...
}
class MockUserService extends UserService {
// Test implementation
}
export const container = defineBuilderConfig({
injections: [
{ token: UserService, provider: MockUserService }
]
});Factory Provider
Use factory functions for dynamic instantiation:
// Arrow functions are auto-detected as factories
{
token: useInterface<IConfig>(),
provider: (container) => ({
apiUrl: process.env.API_URL ?? 'http://localhost',
timeout: 5000
})
}
// Factory with dependencies
{
token: useInterface<IService>(),
provider: (container) => {
const logger = container.resolve(useInterface<ILogger>());
const config = container.resolve(useInterface<IConfig>());
return new MyService(logger, config);
}
}
// Explicit factory flag (for non-arrow functions)
{
token: useInterface<IDatabase>(),
provider: createDatabaseConnection,
useFactory: true
}Property Token
Inject primitive values (string, number, boolean) while keeping classes pure:
import { useProperty } from '@djodjonx/neosyringe';
// Pure class - no DI imports!
class ApiService {
constructor(
private apiUrl: string,
private timeout: number,
private debug: boolean
) {}
}
// Define property tokens
const apiUrl = useProperty<string>(ApiService, 'apiUrl');
const timeout = useProperty<number>(ApiService, 'timeout');
const debug = useProperty<boolean>(ApiService, 'debug');
export const container = defineBuilderConfig({
injections: [
{ token: apiUrl, provider: () => process.env.API_URL ?? 'http://localhost' },
{ token: timeout, provider: () => 5000 },
{ token: debug, provider: () => process.env.NODE_ENV === 'development' },
{ token: ApiService } // Primitives auto-wired!
]
});Type Safety
useProperty(ApiService, 'apiUrl') creates a unique token scoped to that specific class parameter. This means useProperty(ServiceA, 'url') ≠ useProperty(ServiceB, 'url').
Resolving Services
The container.resolve() method provides full type safety without any type assertions:
import { container } from './container';
import { UserService } from './services/user.service';
import { useInterface } from '@djodjonx/neosyringe';
import type { ILogger } from './services/logger';
// ✅ Resolve by class - Type: UserService
const userService = container.resolve(UserService);
userService.createUser('John'); // Full auto-completion!
// ✅ Resolve by interface - Type: ILogger
const logger = container.resolve(useInterface<ILogger>());
logger.log('Hello'); // ILogger methods available
// ✅ Resolve by property - Type: string
const apiUrl = container.resolve(useProperty<string>(ApiService, 'apiUrl'));
apiUrl.toUpperCase(); // String methods workType Inference
TypeScript automatically infers the return type from the token:
// No type assertion needed!
const service = container.resolve(UserService);
// Type: UserService ✅
// Compare with other DI libraries:
// ❌ const service = container.get('UserService') as UserService;IDE Support
Your IDE will provide full auto-completion for resolved services. No more guessing what methods are available!
Partials (Modular Configuration)
Split configuration into reusable modules:
// logging.partial.ts
import { definePartialConfig, useInterface } from '@djodjonx/neosyringe';
export const loggingConfig = definePartialConfig({
injections: [
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
]
});
// database.partial.ts
export const databaseConfig = definePartialConfig({
injections: [
{ token: useInterface<IDatabase>(), provider: PostgresDatabase }
]
});
// container.ts
export const container = defineBuilderConfig({
extends: [loggingConfig, databaseConfig], // Inherit injections
injections: [
{ token: UserService },
{ token: OrderService }
]
});Complete Example
// interfaces.ts
export interface ILogger {
log(msg: string): void;
}
export interface IUserRepository {
findById(id: string): User | null;
}
// implementations.ts
export class ConsoleLogger implements ILogger {
log(msg: string) { console.log(`[LOG] ${msg}`); }
}
export class InMemoryUserRepository implements IUserRepository {
private users = new Map<string, User>();
findById(id: string) {
return this.users.get(id) ?? null;
}
}
// services.ts
export class UserService {
constructor(
private logger: ILogger,
private repo: IUserRepository
) {}
getUser(id: string) {
this.logger.log(`Fetching user ${id}`);
return this.repo.findById(id);
}
}
// container.ts
import { defineBuilderConfig, useInterface } from '@djodjonx/neosyringe';
export const container = defineBuilderConfig({
name: 'AppContainer',
injections: [
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
{ token: useInterface<IUserRepository>(), provider: InMemoryUserRepository },
{ token: UserService }
]
});
// main.ts
import { container } from './container';
const userService = container.resolve(UserService);
const user = userService.getUser('123');