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