or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

binding.mdconditional.mdcontainer.mddecorators.mdindex.mdlifecycle.mdmodules.md
tile.json

binding.mddocs/

0

# Binding Configuration

1

2

InversifyJS provides a fluent API for configuring service bindings, allowing you to specify how services are created, their lifecycle scope, and conditional resolution rules. The binding system supports various patterns from simple class binding to complex factory configurations.

3

4

## Fluent Binding Syntax

5

6

### BindToFluentSyntax

7

8

Defines what the service identifier should be bound to.

9

10

```typescript { .api }

11

interface BindToFluentSyntax<T> {

12

to(constructor: Newable<T>): BindInWhenOnFluentSyntax<T>;

13

toSelf(): BindInWhenOnFluentSyntax<T>;

14

toConstantValue(value: T): BindWhenOnFluentSyntax<T>;

15

toDynamicValue(func: (context: ResolutionContext) => T): BindInWhenOnFluentSyntax<T>;

16

toFactory<T2>(factory: Factory<T2>): BindWhenOnFluentSyntax<T>;

17

toProvider<T2>(provider: Provider<T2>): BindWhenOnFluentSyntax<T>;

18

}

19

```

20

21

### BindInFluentSyntax

22

23

Defines the scope (lifetime) of the binding.

24

25

```typescript { .api }

26

interface BindInFluentSyntax<T> {

27

inSingletonScope(): BindWhenOnFluentSyntax<T>;

28

inTransientScope(): BindWhenOnFluentSyntax<T>;

29

inRequestScope(): BindWhenOnFluentSyntax<T>;

30

}

31

```

32

33

### Combined Syntax Interfaces

34

35

```typescript { .api }

36

interface BindInWhenOnFluentSyntax<T> extends BindInFluentSyntax<T>, BindWhenFluentSyntax<T>, BindOnFluentSyntax<T> {}

37

interface BindWhenOnFluentSyntax<T> extends BindWhenFluentSyntax<T>, BindOnFluentSyntax<T> {}

38

```

39

40

## Binding Targets

41

42

### Bind to Class Constructor

43

44

Bind a service identifier to a specific class constructor.

45

46

```typescript

47

interface IWarrior {

48

fight(): string;

49

}

50

51

@injectable()

52

class Ninja implements IWarrior {

53

fight() { return "Ninja fighting!"; }

54

}

55

56

// Bind interface to implementation

57

container.bind<IWarrior>("Warrior").to(Ninja);

58

```

59

60

### Bind to Self

61

62

Bind a class to itself, useful when no interface abstraction is needed.

63

64

```typescript

65

@injectable()

66

class DatabaseService {

67

connect() { /* ... */ }

68

}

69

70

// Bind class to itself

71

container.bind(DatabaseService).toSelf();

72

73

// Alternative syntax

74

container.bind<DatabaseService>("DatabaseService").to(DatabaseService);

75

```

76

77

### Bind to Constant Value

78

79

Bind to a pre-existing value or configuration object.

80

81

```typescript

82

// Simple constant

83

container.bind<string>("DatabaseUrl").toConstantValue("mongodb://localhost:27017");

84

85

// Configuration object

86

const config = {

87

apiKey: "secret-key",

88

endpoint: "https://api.example.com",

89

timeout: 5000

90

};

91

container.bind<typeof config>("Config").toConstantValue(config);

92

93

// Array of values

94

container.bind<string[]>("AllowedOrigins").toConstantValue([

95

"http://localhost:3000",

96

"https://example.com"

97

]);

98

```

99

100

### Bind to Dynamic Value

101

102

Bind to a function that computes the value at resolution time.

103

104

```typescript

105

// Current timestamp

106

container.bind<Date>("CurrentTime").toDynamicValue(() => new Date());

107

108

// Environment-based configuration

109

container.bind<string>("DatabaseUrl").toDynamicValue(() => {

110

return process.env.NODE_ENV === "production"

111

? "mongodb://prod-server:27017"

112

: "mongodb://localhost:27017";

113

});

114

115

// Context-aware values

116

container.bind<Logger>("Logger").toDynamicValue((context) => {

117

const parentName = context.currentRequest.parentRequest?.serviceIdentifier;

118

return new Logger(`[${parentName}]`);

119

});

120

```

121

122

### Bind to Factory

123

124

Bind to a factory function for complex object creation.

125

126

```typescript

127

// Factory type definition

128

type Factory<T> = () => T;

129

130

// Database connection factory

131

container.bind<Factory<IConnection>>("ConnectionFactory").toFactory(() => {

132

return () => new DatabaseConnection(process.env.DATABASE_URL);

133

});

134

135

// Usage

136

const connectionFactory = container.get<Factory<IConnection>>("ConnectionFactory");

137

const connection = connectionFactory();

138

139

// Factory with dependencies

140

container.bind<Factory<IEmailService>>("EmailFactory").toFactory((context) => {

141

return () => {

142

const config = context.container.get<IConfig>("Config");

143

const logger = context.container.get<ILogger>("Logger");

144

return new EmailService(config, logger);

145

};

146

});

147

```

148

149

### Bind to Provider

150

151

Bind to an async provider function for asynchronous initialization.

152

153

```typescript

154

// Provider type definition

155

type Provider<T> = () => Promise<T>;

156

157

// Async database connection

158

container.bind<Provider<IDatabase>>("DatabaseProvider").toProvider(() => {

159

return async () => {

160

const db = new Database();

161

await db.connect();

162

await db.migrate();

163

return db;

164

};

165

});

166

167

// Usage

168

const dbProvider = container.get<Provider<IDatabase>>("DatabaseProvider");

169

const database = await dbProvider();

170

171

// Provider with dependencies

172

container.bind<Provider<ICache>>("CacheProvider").toProvider((context) => {

173

return async () => {

174

const config = context.container.get<IConfig>("Config");

175

const cache = new RedisCache(config.redis);

176

await cache.connect();

177

return cache;

178

};

179

});

180

```

181

182

## Binding Scopes

183

184

### Singleton Scope

185

186

Creates a single instance shared across the entire container.

187

188

```typescript

189

@injectable()

190

class DatabaseService {

191

private connection?: IConnection;

192

193

async connect() {

194

if (!this.connection) {

195

this.connection = await createConnection();

196

}

197

return this.connection;

198

}

199

}

200

201

// Single instance for entire application

202

container.bind<DatabaseService>("Database").to(DatabaseService).inSingletonScope();

203

204

// Both resolve to same instance

205

const db1 = container.get<DatabaseService>("Database");

206

const db2 = container.get<DatabaseService>("Database");

207

console.log(db1 === db2); // true

208

```

209

210

### Transient Scope

211

212

Creates a new instance every time the service is requested.

213

214

```typescript

215

@injectable()

216

class RequestHandler {

217

private id = Math.random();

218

219

getId() { return this.id; }

220

}

221

222

// New instance every time

223

container.bind<RequestHandler>("Handler").to(RequestHandler).inTransientScope();

224

225

const handler1 = container.get<RequestHandler>("Handler");

226

const handler2 = container.get<RequestHandler>("Handler");

227

console.log(handler1.getId() === handler2.getId()); // false

228

```

229

230

### Request Scope

231

232

Creates one instance per resolution request (dependency graph resolution).

233

234

```typescript

235

@injectable()

236

class RequestContext {

237

private requestId = Math.random();

238

239

getRequestId() { return this.requestId; }

240

}

241

242

@injectable()

243

class ServiceA {

244

constructor(@inject("RequestContext") private context: RequestContext) {}

245

246

getContextId() { return this.context.getRequestId(); }

247

}

248

249

@injectable()

250

class ServiceB {

251

constructor(@inject("RequestContext") private context: RequestContext) {}

252

253

getContextId() { return this.context.getRequestId(); }

254

}

255

256

container.bind<RequestContext>("RequestContext").to(RequestContext).inRequestScope();

257

container.bind<ServiceA>("ServiceA").to(ServiceA);

258

container.bind<ServiceB>("ServiceB").to(ServiceB);

259

260

// Same request context shared within single resolution

261

const serviceA = container.get<ServiceA>("ServiceA");

262

const serviceB = container.get<ServiceB>("ServiceB");

263

console.log(serviceA.getContextId() === serviceB.getContextId()); // true

264

265

// Different request context for new resolution

266

const serviceA2 = container.get<ServiceA>("ServiceA");

267

console.log(serviceA.getContextId() === serviceA2.getContextId()); // false

268

```

269

270

## Advanced Binding Patterns

271

272

### Multiple Bindings

273

274

Bind multiple implementations to the same service identifier.

275

276

```typescript

277

interface IPlugin {

278

execute(): void;

279

}

280

281

@injectable() class PluginA implements IPlugin { execute() { /* ... */ } }

282

@injectable() class PluginB implements IPlugin { execute() { /* ... */ } }

283

@injectable() class PluginC implements IPlugin { execute() { /* ... */ } }

284

285

// Multiple bindings

286

container.bind<IPlugin>("Plugin").to(PluginA);

287

container.bind<IPlugin>("Plugin").to(PluginB);

288

container.bind<IPlugin>("Plugin").to(PluginC);

289

290

// Get all implementations

291

const plugins = container.getAll<IPlugin>("Plugin");

292

plugins.forEach(plugin => plugin.execute());

293

```

294

295

### Binding Identifier Types

296

297

```typescript { .api }

298

interface BindingIdentifier<T = any> {

299

serviceIdentifier: ServiceIdentifier<T>;

300

name?: string;

301

tags?: { [key: string]: any };

302

}

303

304

type BindingActivation<T> = (context: ResolutionContext, injectable: T) => T | Promise<T>;

305

type BindingDeactivation<T> = (injectable: T) => void | Promise<void>;

306

307

interface BindingConstraints {

308

(request: Request): boolean;

309

}

310

```

311

312

### Contextual Binding Information

313

314

Access binding context during dynamic value creation:

315

316

```typescript

317

container.bind<string>("ContextInfo").toDynamicValue((context) => {

318

const request = context.currentRequest;

319

return `Service: ${request.serviceIdentifier.toString()}, Target: ${request.target?.name}`;

320

});

321

```

322

323

## Binding Syntax Chaining

324

325

The fluent syntax allows chaining multiple configuration methods:

326

327

```typescript

328

container

329

.bind<IEmailService>("EmailService")

330

.to(EmailService)

331

.inSingletonScope()

332

.whenTargetNamed("primary")

333

.onActivation((context, service) => {

334

console.log("Email service activated");

335

return service;

336

});

337

```

338

339

## Best Practices

340

341

1. **Use appropriate scopes**: Singleton for shared resources, Transient for stateless services

342

2. **Prefer constructor injection over factory**: Simpler and more testable

343

3. **Use constants for configuration**: Avoid hardcoding values in classes

344

4. **Leverage dynamic values**: For environment-specific or computed values

345

5. **Chain binding methods**: Create readable binding configurations

346

6. **Consider request scope**: For request-specific shared state

347

7. **Use factories sparingly**: Only when complex initialization is required