@dangao/bun-server 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api.md +194 -81
- package/docs/extensions.md +53 -0
- package/docs/guide.md +243 -1
- package/docs/microservice-config-center.md +73 -74
- package/docs/microservice-nacos.md +89 -90
- package/docs/microservice-service-registry.md +85 -86
- package/docs/microservice.md +142 -137
- package/docs/request-lifecycle.md +45 -4
- package/docs/symbol-interface-pattern.md +106 -106
- package/docs/zh/api.md +458 -18
- package/docs/zh/extensions.md +53 -0
- package/docs/zh/guide.md +251 -4
- package/docs/zh/microservice-config-center.md +258 -0
- package/docs/zh/microservice-nacos.md +346 -0
- package/docs/zh/microservice-service-registry.md +306 -0
- package/docs/zh/microservice.md +680 -0
- package/docs/zh/request-lifecycle.md +43 -5
- package/package.json +1 -1
- package/tests/auth/auth-decorators.test.ts +241 -0
- package/tests/auth/oauth2-service.test.ts +318 -0
- package/tests/cache/cache-decorators-extended.test.ts +272 -0
- package/tests/cache/cache-interceptors.test.ts +534 -0
- package/tests/cache/cache-service-proxy.test.ts +246 -0
- package/tests/cache/memory-cache-store.test.ts +155 -0
- package/tests/cache/redis-cache-store.test.ts +199 -0
- package/tests/config/config-center-integration.test.ts +334 -0
- package/tests/config/config-module-extended.test.ts +165 -0
- package/tests/controller/param-binder.test.ts +333 -0
- package/tests/error/error-handler.test.ts +166 -57
- package/tests/error/i18n-extended.test.ts +105 -0
- package/tests/events/event-listener-scanner.test.ts +114 -0
- package/tests/events/event-module.test.ts +133 -302
- package/tests/extensions/logger-module.test.ts +158 -0
- package/tests/files/file-storage.test.ts +136 -0
- package/tests/interceptor/base-interceptor.test.ts +605 -0
- package/tests/interceptor/builtin/cache-interceptor.test.ts +233 -86
- package/tests/interceptor/builtin/log-interceptor.test.ts +469 -0
- package/tests/interceptor/builtin/permission-interceptor.test.ts +219 -120
- package/tests/interceptor/interceptor-chain.test.ts +241 -189
- package/tests/interceptor/interceptor-metadata.test.ts +221 -0
- package/tests/microservice/circuit-breaker.test.ts +221 -0
- package/tests/microservice/service-client-decorators.test.ts +86 -0
- package/tests/microservice/service-client-interceptors.test.ts +274 -0
- package/tests/microservice/service-registry-decorators.test.ts +147 -0
- package/tests/microservice/tracer.test.ts +213 -0
- package/tests/microservice/tracing-collectors.test.ts +168 -0
- package/tests/middleware/builtin/middleware-builtin-extended.test.ts +237 -0
- package/tests/middleware/builtin/rate-limit.test.ts +257 -0
- package/tests/middleware/middleware-decorators.test.ts +222 -0
- package/tests/middleware/middleware-pipeline.test.ts +160 -0
- package/tests/queue/queue-decorators.test.ts +139 -0
- package/tests/queue/queue-service.test.ts +191 -0
- package/tests/request/body-parser-extended.test.ts +291 -0
- package/tests/request/request-wrapper.test.ts +319 -0
- package/tests/router/router-decorators.test.ts +260 -0
- package/tests/router/router-extended.test.ts +298 -0
- package/tests/security/guards/reflector.test.ts +188 -0
- package/tests/security/security-filter.test.ts +182 -0
- package/tests/security/security-module-extended.test.ts +133 -0
- package/tests/session/memory-session-store.test.ts +172 -0
- package/tests/session/session-decorators.test.ts +163 -0
- package/tests/swagger/ui.test.ts +212 -0
|
@@ -20,6 +20,10 @@ HTTP Request
|
|
|
20
20
|
└─────────────────────────────────────┘
|
|
21
21
|
↓
|
|
22
22
|
┌─────────────────────────────────────┐
|
|
23
|
+
│ Guards │
|
|
24
|
+
└─────────────────────────────────────┘
|
|
25
|
+
↓
|
|
26
|
+
┌─────────────────────────────────────┐
|
|
23
27
|
│ Interceptors (Pre) │
|
|
24
28
|
└─────────────────────────────────────┘
|
|
25
29
|
↓
|
|
@@ -79,6 +83,7 @@ class ApiController {
|
|
|
79
83
|
| `createCorsMiddleware` | CORS headers |
|
|
80
84
|
| `createErrorHandlingMiddleware` | Global error handling |
|
|
81
85
|
| `createRateLimitMiddleware` | Rate limiting |
|
|
86
|
+
| `createHttpMetricsMiddleware` | HTTP metrics collection |
|
|
82
87
|
| `createStaticFileMiddleware` | Static file serving |
|
|
83
88
|
| `createFileUploadMiddleware` | File upload handling |
|
|
84
89
|
| `createSessionMiddleware` | Session management |
|
|
@@ -140,6 +145,40 @@ class UserController {
|
|
|
140
145
|
|
|
141
146
|
The router matches the request path and method to a registered route.
|
|
142
147
|
|
|
148
|
+
## 4. Guards
|
|
149
|
+
|
|
150
|
+
Guards execute after routing and before interceptors, providing fine-grained access control. They have access to the `ExecutionContext` which provides rich information about the current request.
|
|
151
|
+
|
|
152
|
+
### Execution Order
|
|
153
|
+
|
|
154
|
+
1. **Global Guards** - Registered via `SecurityModule.forRoot({ globalGuards: [...] })`
|
|
155
|
+
2. **Controller Guards** - Applied via `@UseGuards()` on controller class
|
|
156
|
+
3. **Method Guards** - Applied via `@UseGuards()` on method
|
|
157
|
+
|
|
158
|
+
### Built-in Guards
|
|
159
|
+
|
|
160
|
+
- `AuthGuard`: Requires authentication
|
|
161
|
+
- `OptionalAuthGuard`: Optional authentication
|
|
162
|
+
- `RolesGuard`: Role-based authorization (used with `@Roles()` decorator)
|
|
163
|
+
|
|
164
|
+
### Example
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
@Controller('/api/admin')
|
|
168
|
+
@UseGuards(AuthGuard, RolesGuard)
|
|
169
|
+
class AdminController {
|
|
170
|
+
@GET('/dashboard')
|
|
171
|
+
@Roles('admin')
|
|
172
|
+
public dashboard() {
|
|
173
|
+
return { message: 'Admin Dashboard' };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
For detailed documentation, see [Guards](./guards.md).
|
|
179
|
+
|
|
180
|
+
## 5. Interceptors (Pre-processing)
|
|
181
|
+
|
|
143
182
|
### Matching Priority
|
|
144
183
|
|
|
145
184
|
1. **Static Routes** - Exact path match (`/api/users`)
|
|
@@ -200,7 +239,7 @@ class ApiController {}
|
|
|
200
239
|
- Response transformation
|
|
201
240
|
- Performance monitoring
|
|
202
241
|
|
|
203
|
-
##
|
|
242
|
+
## 6. Parameter Binding and Validation
|
|
204
243
|
|
|
205
244
|
### Parameter Decorators
|
|
206
245
|
|
|
@@ -208,9 +247,11 @@ class ApiController {}
|
|
|
208
247
|
|-----------|--------|---------|
|
|
209
248
|
| `@Param(name)` | URL path parameter | `/users/:id` → `@Param('id')` |
|
|
210
249
|
| `@Query(name)` | Query string | `?page=1` → `@Query('page')` |
|
|
250
|
+
| `@QueryMap()` | All query parameters | `?page=1&limit=10` → `@QueryMap()` returns `{ page: '1', limit: '10' }` |
|
|
211
251
|
| `@Body()` | Request body | JSON body |
|
|
212
252
|
| `@Body(name)` | Body property | `body.name` → `@Body('name')` |
|
|
213
253
|
| `@Header(name)` | Request header | `@Header('Authorization')` |
|
|
254
|
+
| `@HeaderMap()` | All headers | `@HeaderMap()` returns all headers as object |
|
|
214
255
|
| `@Context()` | Full context | Request context object |
|
|
215
256
|
| `@Session()` | Session data | Session object |
|
|
216
257
|
|
|
@@ -262,7 +303,7 @@ When validation fails, a `ValidationError` is thrown with detailed information:
|
|
|
262
303
|
}
|
|
263
304
|
```
|
|
264
305
|
|
|
265
|
-
##
|
|
306
|
+
## 7. Controller Method Execution
|
|
266
307
|
|
|
267
308
|
After validation, the controller method is invoked with resolved dependencies and bound parameters.
|
|
268
309
|
|
|
@@ -294,7 +335,7 @@ Controller methods can return:
|
|
|
294
335
|
- **void** - Empty response (204)
|
|
295
336
|
- **Promise** - Async operations
|
|
296
337
|
|
|
297
|
-
##
|
|
338
|
+
## 8. Interceptors (Post-processing)
|
|
298
339
|
|
|
299
340
|
After the handler executes, post-interceptors run in reverse order:
|
|
300
341
|
|
|
@@ -318,7 +359,7 @@ class TransformInterceptor implements Interceptor {
|
|
|
318
359
|
}
|
|
319
360
|
```
|
|
320
361
|
|
|
321
|
-
##
|
|
362
|
+
## 9. Exception Filter
|
|
322
363
|
|
|
323
364
|
If any exception is thrown during the request lifecycle, it's caught by the exception filter.
|
|
324
365
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# Symbol + Interface
|
|
1
|
+
# Symbol + Interface Same-Name Pattern Explained
|
|
2
2
|
|
|
3
|
-
## 📖
|
|
3
|
+
## 📖 Background
|
|
4
4
|
|
|
5
|
-
TypeScript
|
|
5
|
+
After TypeScript is compiled to JavaScript, all type information is lost. This poses a challenge for dependency injection frameworks: how to identify the type of injected dependencies at runtime?
|
|
6
6
|
|
|
7
|
-
## 🎯
|
|
7
|
+
## 🎯 Solution
|
|
8
8
|
|
|
9
|
-
Bun Server Framework
|
|
9
|
+
Bun Server Framework adopts the **Symbol + Interface Same-Name Pattern** to elegantly solve this problem.
|
|
10
10
|
|
|
11
|
-
## 💡
|
|
11
|
+
## 💡 Core Concepts
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### Problems with Traditional Approach
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
// ❌
|
|
16
|
+
// ❌ Traditional approach: can only inject concrete classes
|
|
17
17
|
interface UserService {
|
|
18
18
|
find(id: string): Promise<User>;
|
|
19
19
|
}
|
|
@@ -23,102 +23,102 @@ class UserServiceImpl implements UserService {
|
|
|
23
23
|
async find(id: string) { ... }
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
//
|
|
27
|
-
//
|
|
26
|
+
// Problem: TypeScript interfaces disappear after compilation
|
|
27
|
+
// Cannot inject using interface type at runtime
|
|
28
28
|
public constructor(
|
|
29
|
-
private readonly userService: UserService //
|
|
29
|
+
private readonly userService: UserService // Type information lost after compilation
|
|
30
30
|
) {}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### Symbol + Interface
|
|
33
|
+
### Symbol + Interface Same-Name Pattern
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
|
-
// ✅ Bun Server
|
|
36
|
+
// ✅ Bun Server approach: Symbol + Interface same name
|
|
37
37
|
|
|
38
|
-
// 1.
|
|
38
|
+
// 1. Define interface (compile-time type checking)
|
|
39
39
|
interface UserService {
|
|
40
40
|
find(id: string): Promise<User>;
|
|
41
41
|
create(user: User): Promise<User>;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// 2.
|
|
45
|
-
//
|
|
44
|
+
// 2. Define same-name Symbol (runtime Token)
|
|
45
|
+
// Note: declared as const, same name as interface
|
|
46
46
|
const UserService = Symbol('UserService');
|
|
47
47
|
|
|
48
|
-
// 3.
|
|
48
|
+
// 3. Implement interface
|
|
49
49
|
@Injectable()
|
|
50
50
|
class UserServiceImpl implements UserService {
|
|
51
51
|
public async find(id: string): Promise<User> {
|
|
52
|
-
//
|
|
52
|
+
// Implementation...
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
public async create(user: User): Promise<User> {
|
|
56
|
-
//
|
|
56
|
+
// Implementation...
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// 4.
|
|
60
|
+
// 4. Configure in Module
|
|
61
61
|
@Module({
|
|
62
62
|
providers: [{
|
|
63
|
-
provide: UserService, // Symbol Token
|
|
64
|
-
useClass: UserServiceImpl, //
|
|
63
|
+
provide: UserService, // Symbol Token (runtime)
|
|
64
|
+
useClass: UserServiceImpl, // Implementation class
|
|
65
65
|
}],
|
|
66
|
-
exports: [UserServiceImpl], //
|
|
66
|
+
exports: [UserServiceImpl], // Export implementation class (optional)
|
|
67
67
|
})
|
|
68
68
|
class UserModule {}
|
|
69
69
|
|
|
70
|
-
// 5.
|
|
70
|
+
// 5. Inject and use
|
|
71
71
|
@Controller('/users')
|
|
72
72
|
class UserController {
|
|
73
73
|
public constructor(
|
|
74
|
-
//
|
|
75
|
-
//
|
|
74
|
+
// Type is interface UserService (compile-time check)
|
|
75
|
+
// Actually injected is instance corresponding to Symbol('UserService') (runtime)
|
|
76
76
|
private readonly userService: UserService,
|
|
77
77
|
) {}
|
|
78
78
|
|
|
79
79
|
@GET('/:id')
|
|
80
80
|
public async getUser(@Param('id') id: string) {
|
|
81
|
-
// TypeScript
|
|
81
|
+
// TypeScript knows userService has find method
|
|
82
82
|
return await this.userService.find(id);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
## 🔑
|
|
87
|
+
## 🔑 Key Points
|
|
88
88
|
|
|
89
|
-
### 1.
|
|
89
|
+
### 1. Cannot Use `import type` When Importing
|
|
90
90
|
|
|
91
91
|
```typescript
|
|
92
|
-
// ✅
|
|
92
|
+
// ✅ Correct: Import both Symbol and interface
|
|
93
93
|
import { UserService } from './user-service';
|
|
94
94
|
|
|
95
|
-
// ❌
|
|
95
|
+
// ❌ Wrong: Only import type, Symbol is lost
|
|
96
96
|
import type { UserService } from './user-service';
|
|
97
97
|
|
|
98
|
-
// ❌
|
|
98
|
+
// ❌ Wrong: Mixed import causes confusion
|
|
99
99
|
import { type UserService } from './user-service';
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
**Reason**: `import type` only imports type information, which is completely removed after compilation, causing the Symbol to be lost.
|
|
103
103
|
|
|
104
|
-
### 2.
|
|
104
|
+
### 2. Export Order
|
|
105
105
|
|
|
106
106
|
```typescript
|
|
107
|
-
//
|
|
107
|
+
// Recommended file organization
|
|
108
108
|
|
|
109
109
|
// user-service.ts
|
|
110
|
-
// 1.
|
|
110
|
+
// 1. Import dependencies
|
|
111
111
|
import { Injectable } from '@dangao/bun-server';
|
|
112
112
|
|
|
113
|
-
// 2.
|
|
113
|
+
// 2. Define interface
|
|
114
114
|
export interface UserService {
|
|
115
115
|
find(id: string): Promise<User>;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// 3.
|
|
118
|
+
// 3. Define Symbol (same name as interface)
|
|
119
119
|
export const UserService = Symbol('UserService');
|
|
120
120
|
|
|
121
|
-
// 4.
|
|
121
|
+
// 4. Implementation class
|
|
122
122
|
@Injectable()
|
|
123
123
|
export class UserServiceImpl implements UserService {
|
|
124
124
|
public async find(id: string): Promise<User> {
|
|
@@ -127,66 +127,66 @@ export class UserServiceImpl implements UserService {
|
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
### 3. Module
|
|
130
|
+
### 3. Module Configuration
|
|
131
131
|
|
|
132
132
|
```typescript
|
|
133
133
|
@Module({
|
|
134
134
|
providers: [
|
|
135
135
|
{
|
|
136
|
-
provide: UserService, //
|
|
137
|
-
useClass: UserServiceImpl, //
|
|
136
|
+
provide: UserService, // Use Symbol as Token
|
|
137
|
+
useClass: UserServiceImpl, // Specify implementation class
|
|
138
138
|
}
|
|
139
139
|
],
|
|
140
|
-
exports: [UserServiceImpl], //
|
|
140
|
+
exports: [UserServiceImpl], // Export implementation class (for other modules to use)
|
|
141
141
|
})
|
|
142
142
|
class UserModule {}
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
- `provide`
|
|
147
|
-
- `exports`
|
|
145
|
+
**Note**:
|
|
146
|
+
- `provide` uses Symbol Token
|
|
147
|
+
- `exports` exports implementation class (not Symbol)
|
|
148
148
|
|
|
149
|
-
### 4.
|
|
149
|
+
### 4. Constructor Injection
|
|
150
150
|
|
|
151
151
|
```typescript
|
|
152
|
-
// ✅
|
|
152
|
+
// ✅ Recommended: Default injection (no decorator needed)
|
|
153
153
|
public constructor(
|
|
154
|
-
private readonly userService: UserService, //
|
|
154
|
+
private readonly userService: UserService, // Framework automatically recognizes type
|
|
155
155
|
) {}
|
|
156
156
|
|
|
157
|
-
// ⚠️
|
|
157
|
+
// ⚠️ Only use @Inject in the following cases
|
|
158
158
|
public constructor(
|
|
159
159
|
@Inject(UserService) private readonly userService: UserService,
|
|
160
160
|
) {}
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
## 📋
|
|
163
|
+
## 📋 Complete Example
|
|
164
164
|
|
|
165
|
-
###
|
|
165
|
+
### Step 1: Define Service Interface and Implementation
|
|
166
166
|
|
|
167
167
|
```typescript
|
|
168
168
|
// src/user/user-service.ts
|
|
169
169
|
|
|
170
170
|
import { Injectable } from '@dangao/bun-server';
|
|
171
171
|
|
|
172
|
-
// 1.
|
|
172
|
+
// 1. Define user entity
|
|
173
173
|
export interface User {
|
|
174
174
|
id: string;
|
|
175
175
|
name: string;
|
|
176
176
|
email: string;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
// 2.
|
|
179
|
+
// 2. Define service interface
|
|
180
180
|
export interface UserService {
|
|
181
181
|
find(id: string): Promise<User | undefined>;
|
|
182
182
|
create(name: string, email: string): Promise<User>;
|
|
183
183
|
findAll(): Promise<User[]>;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
// 3.
|
|
186
|
+
// 3. Define same-name Symbol
|
|
187
187
|
export const UserService = Symbol('UserService');
|
|
188
188
|
|
|
189
|
-
// 4.
|
|
189
|
+
// 4. Implement service
|
|
190
190
|
@Injectable()
|
|
191
191
|
export class UserServiceImpl implements UserService {
|
|
192
192
|
private readonly users = new Map<string, User>();
|
|
@@ -208,18 +208,18 @@ export class UserServiceImpl implements UserService {
|
|
|
208
208
|
}
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
-
###
|
|
211
|
+
### Step 2: Create Controller
|
|
212
212
|
|
|
213
213
|
```typescript
|
|
214
214
|
// src/user/user-controller.ts
|
|
215
215
|
|
|
216
216
|
import { Controller, GET, POST, Body, Param } from '@dangao/bun-server';
|
|
217
|
-
// ✅
|
|
217
|
+
// ✅ Note: Don't use import type
|
|
218
218
|
import { UserService } from './user-service';
|
|
219
219
|
|
|
220
220
|
@Controller('/api/users')
|
|
221
221
|
export class UserController {
|
|
222
|
-
//
|
|
222
|
+
// Constructor injection, framework automatically recognizes type
|
|
223
223
|
public constructor(
|
|
224
224
|
private readonly userService: UserService,
|
|
225
225
|
) {}
|
|
@@ -245,7 +245,7 @@ export class UserController {
|
|
|
245
245
|
}
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
###
|
|
248
|
+
### Step 3: Configure Module
|
|
249
249
|
|
|
250
250
|
```typescript
|
|
251
251
|
// src/user/user-module.ts
|
|
@@ -259,15 +259,15 @@ import { UserService, UserServiceImpl } from './user-service';
|
|
|
259
259
|
providers: [
|
|
260
260
|
{
|
|
261
261
|
provide: UserService, // Symbol Token
|
|
262
|
-
useClass: UserServiceImpl, //
|
|
262
|
+
useClass: UserServiceImpl, // Implementation class
|
|
263
263
|
}
|
|
264
264
|
],
|
|
265
|
-
exports: [UserServiceImpl], //
|
|
265
|
+
exports: [UserServiceImpl], // Export for other modules to use
|
|
266
266
|
})
|
|
267
267
|
export class UserModule {}
|
|
268
268
|
```
|
|
269
269
|
|
|
270
|
-
###
|
|
270
|
+
### Step 4: Start Application
|
|
271
271
|
|
|
272
272
|
```typescript
|
|
273
273
|
// src/main.ts
|
|
@@ -282,19 +282,19 @@ app.listen();
|
|
|
282
282
|
console.log('Server running on http://localhost:3000');
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
-
## 🎨
|
|
285
|
+
## 🎨 Advanced Usage
|
|
286
286
|
|
|
287
|
-
###
|
|
287
|
+
### Multiple Implementation Switching
|
|
288
288
|
|
|
289
289
|
```typescript
|
|
290
|
-
//
|
|
290
|
+
// Define interface and Symbol
|
|
291
291
|
export interface CacheService {
|
|
292
292
|
get(key: string): Promise<string | null>;
|
|
293
293
|
set(key: string, value: string): Promise<void>;
|
|
294
294
|
}
|
|
295
295
|
export const CacheService = Symbol('CacheService');
|
|
296
296
|
|
|
297
|
-
//
|
|
297
|
+
// Memory implementation
|
|
298
298
|
@Injectable()
|
|
299
299
|
export class MemoryCacheService implements CacheService {
|
|
300
300
|
private cache = new Map<string, string>();
|
|
@@ -308,19 +308,19 @@ export class MemoryCacheService implements CacheService {
|
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
// Redis
|
|
311
|
+
// Redis implementation
|
|
312
312
|
@Injectable()
|
|
313
313
|
export class RedisCacheService implements CacheService {
|
|
314
314
|
async get(key: string) {
|
|
315
|
-
// Redis
|
|
315
|
+
// Redis implementation...
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
async set(key: string, value: string) {
|
|
319
|
-
// Redis
|
|
319
|
+
// Redis implementation...
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
//
|
|
323
|
+
// Switch implementation based on environment
|
|
324
324
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
325
325
|
|
|
326
326
|
@Module({
|
|
@@ -334,10 +334,10 @@ const isProduction = process.env.NODE_ENV === 'production';
|
|
|
334
334
|
export class CacheModule {}
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
###
|
|
337
|
+
### Factory Function
|
|
338
338
|
|
|
339
339
|
```typescript
|
|
340
|
-
//
|
|
340
|
+
// Use factory function to create instances
|
|
341
341
|
@Module({
|
|
342
342
|
providers: [
|
|
343
343
|
{
|
|
@@ -355,54 +355,54 @@ export class CacheModule {}
|
|
|
355
355
|
export class UserModule {}
|
|
356
356
|
```
|
|
357
357
|
|
|
358
|
-
## ❓
|
|
358
|
+
## ❓ Common Questions
|
|
359
359
|
|
|
360
|
-
### Q1:
|
|
360
|
+
### Q1: Why Not Use Classes Directly as Tokens?
|
|
361
361
|
|
|
362
|
-
**A**:
|
|
363
|
-
1.
|
|
364
|
-
2.
|
|
365
|
-
3.
|
|
362
|
+
**A**: Using classes as tokens has the following problems:
|
|
363
|
+
1. Cannot implement interface-oriented programming
|
|
364
|
+
2. Tight coupling to implementation classes, not conducive to testing
|
|
365
|
+
3. Cannot dynamically switch implementations at runtime
|
|
366
366
|
|
|
367
|
-
Symbol + Interface
|
|
367
|
+
The Symbol + Interface pattern provides better flexibility.
|
|
368
368
|
|
|
369
|
-
### Q2: Symbol
|
|
369
|
+
### Q2: What's the Difference Between Symbol and String Tokens?
|
|
370
370
|
|
|
371
371
|
```typescript
|
|
372
|
-
// Symbol Token
|
|
372
|
+
// Symbol Token (recommended)
|
|
373
373
|
const UserService = Symbol('UserService');
|
|
374
374
|
|
|
375
|
-
// String Token
|
|
375
|
+
// String Token (not recommended)
|
|
376
376
|
const USER_SERVICE_TOKEN = 'UserService';
|
|
377
377
|
```
|
|
378
378
|
|
|
379
|
-
|
|
380
|
-
- Symbol
|
|
381
|
-
- String
|
|
382
|
-
- Symbol
|
|
379
|
+
**Differences**:
|
|
380
|
+
- Symbol is unique, avoiding naming conflicts
|
|
381
|
+
- String may be duplicated in large projects, causing injection errors
|
|
382
|
+
- Symbol combined with same-name interface provides clearer semantics
|
|
383
383
|
|
|
384
|
-
### Q3:
|
|
384
|
+
### Q3: When Must @Inject Decorator Be Used?
|
|
385
385
|
|
|
386
|
-
|
|
387
|
-
1.
|
|
388
|
-
2.
|
|
389
|
-
3.
|
|
386
|
+
Only needed in the following cases:
|
|
387
|
+
1. Using Symbol Token (although default injection also supports it, explicit use is clearer)
|
|
388
|
+
2. Parameter type cannot be inferred (e.g., interface)
|
|
389
|
+
3. Need to inject specific implementation
|
|
390
390
|
|
|
391
391
|
```typescript
|
|
392
|
-
//
|
|
392
|
+
// Cases requiring @Inject
|
|
393
393
|
public constructor(
|
|
394
394
|
@Inject(CONFIG_SERVICE_TOKEN) private config: ConfigService,
|
|
395
395
|
@Inject(LOGGER_TOKEN) private logger: Logger,
|
|
396
396
|
) {}
|
|
397
397
|
|
|
398
|
-
//
|
|
398
|
+
// No @Inject needed (recommended)
|
|
399
399
|
public constructor(
|
|
400
400
|
private readonly userService: UserService,
|
|
401
401
|
private readonly productService: ProductService,
|
|
402
402
|
) {}
|
|
403
403
|
```
|
|
404
404
|
|
|
405
|
-
### Q4: exports
|
|
405
|
+
### Q4: Why Does exports Export Implementation Classes Instead of Symbols?
|
|
406
406
|
|
|
407
407
|
```typescript
|
|
408
408
|
@Module({
|
|
@@ -410,22 +410,22 @@ public constructor(
|
|
|
410
410
|
provide: UserService, // Symbol Token
|
|
411
411
|
useClass: UserServiceImpl,
|
|
412
412
|
}],
|
|
413
|
-
exports: [UserServiceImpl], //
|
|
413
|
+
exports: [UserServiceImpl], // Export implementation class
|
|
414
414
|
})
|
|
415
415
|
```
|
|
416
416
|
|
|
417
|
-
|
|
418
|
-
- `exports`
|
|
419
|
-
-
|
|
420
|
-
-
|
|
417
|
+
**Reason**:
|
|
418
|
+
- The purpose of `exports` is to allow other modules to import this module's providers
|
|
419
|
+
- What's exported are elements from the providers array (implementation classes)
|
|
420
|
+
- After other modules import through `imports`, they can use Symbol Token for injection
|
|
421
421
|
|
|
422
|
-
## 📚
|
|
422
|
+
## 📚 Related Resources
|
|
423
423
|
|
|
424
|
-
- [
|
|
425
|
-
- [
|
|
426
|
-
- [
|
|
427
|
-
- [
|
|
424
|
+
- [Dependency Injection Guide](./guide.md#dependency-injection)
|
|
425
|
+
- [Module System Explained](./guide.md#module-system)
|
|
426
|
+
- [Best Practices](./best-practices.md)
|
|
427
|
+
- [Example Code](../examples/basic-app.ts)
|
|
428
428
|
|
|
429
429
|
---
|
|
430
430
|
|
|
431
|
-
|
|
431
|
+
**Tip**: This pattern is one of the core features of Bun Server Framework. Understanding it will help you design more maintainable application architectures.
|