@dangao/bun-server 1.1.2 → 1.1.4
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 +602 -0
- package/docs/best-practices.md +12 -0
- package/docs/custom-decorators.md +440 -0
- package/docs/deployment.md +447 -0
- package/docs/error-handling.md +462 -0
- package/docs/extensions.md +569 -0
- package/docs/guide.md +634 -0
- package/docs/migration.md +10 -0
- package/docs/performance.md +452 -0
- package/docs/troubleshooting.md +286 -0
- package/docs/zh/api.md +168 -0
- package/docs/zh/best-practices.md +38 -0
- package/docs/zh/custom-decorators.md +466 -0
- package/docs/zh/deployment.md +445 -0
- package/docs/zh/error-handling.md +456 -0
- package/docs/zh/extensions.md +584 -0
- package/docs/zh/guide.md +361 -0
- package/docs/zh/migration.md +86 -0
- package/docs/zh/performance.md +451 -0
- package/docs/zh/troubleshooting.md +279 -0
- package/package.json +4 -3
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# Performance Optimization Guide
|
|
2
|
+
|
|
3
|
+
This guide covers performance optimization techniques for Bun Server Framework
|
|
4
|
+
applications.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [Benchmarking](#benchmarking)
|
|
9
|
+
- [Caching Strategies](#caching-strategies)
|
|
10
|
+
- [Database Optimization](#database-optimization)
|
|
11
|
+
- [Middleware Optimization](#middleware-optimization)
|
|
12
|
+
- [Response Optimization](#response-optimization)
|
|
13
|
+
- [Memory Management](#memory-management)
|
|
14
|
+
- [Concurrency](#concurrency)
|
|
15
|
+
|
|
16
|
+
## Benchmarking
|
|
17
|
+
|
|
18
|
+
### Using Performance Harness
|
|
19
|
+
|
|
20
|
+
Use the built-in `PerformanceHarness` for benchmarking:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { PerformanceHarness } from "@dangao/bun-server/testing";
|
|
24
|
+
|
|
25
|
+
const result = await PerformanceHarness.benchmark(
|
|
26
|
+
"my-operation",
|
|
27
|
+
1000,
|
|
28
|
+
async () => {
|
|
29
|
+
// Your code to benchmark
|
|
30
|
+
await doSomething();
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
console.log(`Operations per second: ${result.opsPerSecond}`);
|
|
35
|
+
console.log(`Total duration: ${result.durationMs}ms`);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Stress Testing
|
|
39
|
+
|
|
40
|
+
Use `StressTester` for concurrent load testing:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { StressTester } from "@dangao/bun-server/testing";
|
|
44
|
+
|
|
45
|
+
const result = await StressTester.run(
|
|
46
|
+
"my-endpoint",
|
|
47
|
+
1000, // iterations
|
|
48
|
+
10, // concurrency
|
|
49
|
+
async (iteration) => {
|
|
50
|
+
const response = await fetch("http://localhost:3000/api/test");
|
|
51
|
+
await response.text();
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
console.log(`Errors: ${result.errors}`);
|
|
56
|
+
console.log(`Duration: ${result.durationMs}ms`);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Caching Strategies
|
|
60
|
+
|
|
61
|
+
### Use CacheModule
|
|
62
|
+
|
|
63
|
+
Enable caching for frequently accessed data:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import {
|
|
67
|
+
CACHE_SERVICE_TOKEN,
|
|
68
|
+
CacheModule,
|
|
69
|
+
CacheService,
|
|
70
|
+
} from "@dangao/bun-server";
|
|
71
|
+
|
|
72
|
+
CacheModule.forRoot({
|
|
73
|
+
store: new RedisCacheStore(redisClient), // Use Redis for distributed caching
|
|
74
|
+
defaultTtl: 3600000, // 1 hour
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
@Injectable()
|
|
78
|
+
class ProductService {
|
|
79
|
+
public constructor(
|
|
80
|
+
@Inject(CACHE_SERVICE_TOKEN) private readonly cache: CacheService,
|
|
81
|
+
) {}
|
|
82
|
+
|
|
83
|
+
public async getProduct(id: string) {
|
|
84
|
+
return await this.cache.getOrSet(
|
|
85
|
+
`product:${id}`,
|
|
86
|
+
async () => {
|
|
87
|
+
return await this.db.findProduct(id);
|
|
88
|
+
},
|
|
89
|
+
3600000, // Cache for 1 hour
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Cache Decorators
|
|
96
|
+
|
|
97
|
+
Use cache decorators for automatic caching:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { Cacheable, CacheEvict, CachePut } from "@dangao/bun-server";
|
|
101
|
+
|
|
102
|
+
@Controller("/api/products")
|
|
103
|
+
class ProductController {
|
|
104
|
+
@GET("/:id")
|
|
105
|
+
@Cacheable({ key: "product", ttl: 3600000 })
|
|
106
|
+
public async getProduct(@Param("id") id: string) {
|
|
107
|
+
return await this.productService.find(id);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@PUT("/:id")
|
|
111
|
+
@CachePut({ key: "product" })
|
|
112
|
+
@CacheEvict({ key: "products:list" })
|
|
113
|
+
public async updateProduct(@Param("id") id: string, @Body() data: any) {
|
|
114
|
+
return await this.productService.update(id, data);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Cache Invalidation
|
|
120
|
+
|
|
121
|
+
Implement cache invalidation strategies:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Invalidate on update
|
|
125
|
+
@PUT('/:id')
|
|
126
|
+
@CacheEvict({ key: 'product', pattern: true })
|
|
127
|
+
public async updateProduct(@Param('id') id: string) {
|
|
128
|
+
// Cache for 'product:*' will be invalidated
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Invalidate multiple keys
|
|
132
|
+
@DELETE('/:id')
|
|
133
|
+
@CacheEvict({ keys: ['product', 'products:list'] })
|
|
134
|
+
public async deleteProduct(@Param('id') id: string) {
|
|
135
|
+
// Both caches invalidated
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Database Optimization
|
|
140
|
+
|
|
141
|
+
### Connection Pooling
|
|
142
|
+
|
|
143
|
+
Configure appropriate connection pool size:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
DatabaseModule.forRoot({
|
|
147
|
+
database: {
|
|
148
|
+
type: "postgres",
|
|
149
|
+
config: {
|
|
150
|
+
connectionString: process.env.DATABASE_URL,
|
|
151
|
+
max: 20, // Maximum connections
|
|
152
|
+
min: 5, // Minimum connections
|
|
153
|
+
idleTimeoutMillis: 30000,
|
|
154
|
+
connectionTimeoutMillis: 2000,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Query Optimization
|
|
161
|
+
|
|
162
|
+
Use indexes and optimize queries:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Use indexes
|
|
166
|
+
@Table("users")
|
|
167
|
+
class User {
|
|
168
|
+
@Column({ primaryKey: true })
|
|
169
|
+
public id!: number;
|
|
170
|
+
|
|
171
|
+
@Column({ index: true }) // Add index
|
|
172
|
+
public email!: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Use select specific fields
|
|
176
|
+
const users = await db.select("id", "name").from("users").where("active", true);
|
|
177
|
+
|
|
178
|
+
// Use pagination
|
|
179
|
+
const users = await db
|
|
180
|
+
.select()
|
|
181
|
+
.from("users")
|
|
182
|
+
.limit(20)
|
|
183
|
+
.offset((page - 1) * 20);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Batch Operations
|
|
187
|
+
|
|
188
|
+
Use batch operations for multiple inserts/updates:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Batch insert
|
|
192
|
+
await db.batchInsert("users", [
|
|
193
|
+
{ name: "User 1", email: "user1@example.com" },
|
|
194
|
+
{ name: "User 2", email: "user2@example.com" },
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
// Batch update
|
|
198
|
+
await db.transaction(async (trx) => {
|
|
199
|
+
for (const user of users) {
|
|
200
|
+
await trx("users").where("id", user.id).update(user);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Middleware Optimization
|
|
206
|
+
|
|
207
|
+
### Minimize Middleware
|
|
208
|
+
|
|
209
|
+
Only use necessary middleware:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// ❌ Too many middleware
|
|
213
|
+
app.use(middleware1);
|
|
214
|
+
app.use(middleware2);
|
|
215
|
+
app.use(middleware3);
|
|
216
|
+
app.use(middleware4);
|
|
217
|
+
app.use(middleware5);
|
|
218
|
+
|
|
219
|
+
// ✅ Combine or remove unnecessary middleware
|
|
220
|
+
app.use(combinedMiddleware);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Conditional Middleware
|
|
224
|
+
|
|
225
|
+
Use conditional middleware for better performance:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
app.use(async (ctx, next) => {
|
|
229
|
+
// Skip middleware for static files
|
|
230
|
+
if (ctx.path.startsWith("/static")) {
|
|
231
|
+
return await next();
|
|
232
|
+
}
|
|
233
|
+
// Apply middleware
|
|
234
|
+
return await middleware(ctx, next);
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Async Middleware
|
|
239
|
+
|
|
240
|
+
Ensure middleware is properly async:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// ✅ Correct: Properly await next()
|
|
244
|
+
app.use(async (ctx, next) => {
|
|
245
|
+
const start = Date.now();
|
|
246
|
+
const response = await next();
|
|
247
|
+
const duration = Date.now() - start;
|
|
248
|
+
console.log(`Request took ${duration}ms`);
|
|
249
|
+
return response;
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// ❌ Wrong: Not awaiting next()
|
|
253
|
+
app.use(async (ctx, next) => {
|
|
254
|
+
next(); // Missing await
|
|
255
|
+
return new Response("ok");
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Response Optimization
|
|
260
|
+
|
|
261
|
+
### Compression
|
|
262
|
+
|
|
263
|
+
Enable gzip compression in reverse proxy (Nginx, Caddy):
|
|
264
|
+
|
|
265
|
+
```nginx
|
|
266
|
+
# Nginx
|
|
267
|
+
gzip on;
|
|
268
|
+
gzip_vary on;
|
|
269
|
+
gzip_min_length 1024;
|
|
270
|
+
gzip_types text/plain text/css application/json application/javascript;
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Response Streaming
|
|
274
|
+
|
|
275
|
+
Use streaming for large responses:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
@GET('/large-data')
|
|
279
|
+
public async getLargeData() {
|
|
280
|
+
const stream = new ReadableStream({
|
|
281
|
+
async start(controller) {
|
|
282
|
+
// Stream data in chunks
|
|
283
|
+
for (const chunk of largeData) {
|
|
284
|
+
controller.enqueue(JSON.stringify(chunk));
|
|
285
|
+
}
|
|
286
|
+
controller.close();
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return new Response(stream, {
|
|
291
|
+
headers: {
|
|
292
|
+
'Content-Type': 'application/json',
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Pagination
|
|
299
|
+
|
|
300
|
+
Always paginate large datasets:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
@GET('/users')
|
|
304
|
+
public async getUsers(
|
|
305
|
+
@Query('page') page: number = 1,
|
|
306
|
+
@Query('limit') limit: number = 20,
|
|
307
|
+
) {
|
|
308
|
+
const offset = (page - 1) * limit;
|
|
309
|
+
const users = await this.userService.findAll({ limit, offset });
|
|
310
|
+
return {
|
|
311
|
+
data: users,
|
|
312
|
+
pagination: {
|
|
313
|
+
page,
|
|
314
|
+
limit,
|
|
315
|
+
total: await this.userService.count(),
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Memory Management
|
|
322
|
+
|
|
323
|
+
### Avoid Memory Leaks
|
|
324
|
+
|
|
325
|
+
Clean up resources properly:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Clean up in afterEach/afterAll
|
|
329
|
+
afterEach(async () => {
|
|
330
|
+
await app.stop();
|
|
331
|
+
ModuleRegistry.getInstance().clear();
|
|
332
|
+
RouteRegistry.getInstance().clear();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Clear intervals/timeouts
|
|
336
|
+
const intervalId = setInterval(() => {}, 1000);
|
|
337
|
+
// ... later
|
|
338
|
+
clearInterval(intervalId);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Use TTL for Cache
|
|
342
|
+
|
|
343
|
+
Set appropriate TTL for cache entries:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
CacheModule.forRoot({
|
|
347
|
+
defaultTtl: 3600000, // 1 hour
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Override for specific entries
|
|
351
|
+
await cache.set("key", "value", 60000); // 1 minute
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Monitor Memory Usage
|
|
355
|
+
|
|
356
|
+
Monitor memory usage in production:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { performance } from "node:perf_hooks";
|
|
360
|
+
|
|
361
|
+
setInterval(() => {
|
|
362
|
+
const usage = process.memoryUsage();
|
|
363
|
+
console.log({
|
|
364
|
+
heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
|
|
365
|
+
heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
|
|
366
|
+
rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
|
|
367
|
+
});
|
|
368
|
+
}, 60000); // Every minute
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Concurrency
|
|
372
|
+
|
|
373
|
+
### Use Async/Await
|
|
374
|
+
|
|
375
|
+
Always use async/await for I/O operations:
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// ✅ Correct: Async
|
|
379
|
+
@GET('/users')
|
|
380
|
+
public async getUsers() {
|
|
381
|
+
return await this.userService.findAll();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ❌ Wrong: Synchronous
|
|
385
|
+
@GET('/users')
|
|
386
|
+
public getUsers() {
|
|
387
|
+
return this.userService.findAllSync(); // Blocks event loop
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Parallel Operations
|
|
392
|
+
|
|
393
|
+
Use `Promise.all()` for parallel operations:
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// ✅ Parallel execution
|
|
397
|
+
const [user, profile, settings] = await Promise.all([
|
|
398
|
+
this.userService.find(id),
|
|
399
|
+
this.profileService.find(id),
|
|
400
|
+
this.settingsService.find(id),
|
|
401
|
+
]);
|
|
402
|
+
|
|
403
|
+
// ❌ Sequential execution
|
|
404
|
+
const user = await this.userService.find(id);
|
|
405
|
+
const profile = await this.profileService.find(id);
|
|
406
|
+
const settings = await this.settingsService.find(id);
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Worker Threads
|
|
410
|
+
|
|
411
|
+
Use worker threads for CPU-intensive tasks:
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
import { Worker } from 'worker_threads';
|
|
415
|
+
|
|
416
|
+
@POST('/process')
|
|
417
|
+
public async processData(@Body() data: any) {
|
|
418
|
+
return await new Promise((resolve, reject) => {
|
|
419
|
+
const worker = new Worker('./worker.js', {
|
|
420
|
+
workerData: data,
|
|
421
|
+
});
|
|
422
|
+
worker.on('message', resolve);
|
|
423
|
+
worker.on('error', reject);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Best Practices Summary
|
|
429
|
+
|
|
430
|
+
1. **Cache frequently accessed data** - Use CacheModule with appropriate TTL
|
|
431
|
+
2. **Optimize database queries** - Use indexes, pagination, batch operations
|
|
432
|
+
3. **Minimize middleware** - Only use necessary middleware
|
|
433
|
+
4. **Use async/await** - Never block the event loop
|
|
434
|
+
5. **Enable compression** - Use gzip/brotli compression
|
|
435
|
+
6. **Monitor performance** - Use PerformanceHarness and monitoring tools
|
|
436
|
+
7. **Clean up resources** - Properly clean up in tests and shutdown handlers
|
|
437
|
+
8. **Use connection pooling** - Configure appropriate pool sizes
|
|
438
|
+
9. **Stream large responses** - Use streaming for large data
|
|
439
|
+
10. **Parallelize operations** - Use Promise.all() for independent operations
|
|
440
|
+
|
|
441
|
+
## Performance Checklist
|
|
442
|
+
|
|
443
|
+
- [ ] Caching enabled for frequently accessed data
|
|
444
|
+
- [ ] Database connection pooling configured
|
|
445
|
+
- [ ] Database queries optimized (indexes, pagination)
|
|
446
|
+
- [ ] Middleware minimized and optimized
|
|
447
|
+
- [ ] Compression enabled (gzip/brotli)
|
|
448
|
+
- [ ] Async/await used for all I/O operations
|
|
449
|
+
- [ ] Memory leaks prevented (cleanup handlers)
|
|
450
|
+
- [ ] Performance monitoring enabled
|
|
451
|
+
- [ ] Large responses paginated or streamed
|
|
452
|
+
- [ ] Parallel operations used where possible
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Troubleshooting Guide
|
|
2
|
+
|
|
3
|
+
This guide helps you diagnose and resolve common issues when using Bun Server
|
|
4
|
+
Framework.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [Common Issues](#common-issues)
|
|
9
|
+
- [Module Registration](#module-registration)
|
|
10
|
+
- [Dependency Injection](#dependency-injection)
|
|
11
|
+
- [Routing Issues](#routing-issues)
|
|
12
|
+
- [Performance Issues](#performance-issues)
|
|
13
|
+
- [Debugging Tips](#debugging-tips)
|
|
14
|
+
|
|
15
|
+
## Common Issues
|
|
16
|
+
|
|
17
|
+
### Provider Not Found
|
|
18
|
+
|
|
19
|
+
**Error**: `Provider not found for token: Symbol(...)`
|
|
20
|
+
|
|
21
|
+
**Cause**: A module's `forRoot()` method was called before the `Application`
|
|
22
|
+
instance was created, or the module wasn't registered with
|
|
23
|
+
`app.registerModule()`.
|
|
24
|
+
|
|
25
|
+
**Solution**:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// ❌ Wrong: Module.forRoot() before Application
|
|
29
|
+
ConfigModule.forRoot({ defaultConfig: {} });
|
|
30
|
+
const app = new Application({ port: 3000 });
|
|
31
|
+
|
|
32
|
+
// ✅ Correct: Module.forRoot() before Application, then register
|
|
33
|
+
ConfigModule.forRoot({ defaultConfig: {} });
|
|
34
|
+
const app = new Application({ port: 3000 });
|
|
35
|
+
app.registerModule(ConfigModule);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Module Metadata Not Cleared
|
|
39
|
+
|
|
40
|
+
**Error**: Tests fail with "Module already registered" or unexpected behavior in
|
|
41
|
+
tests.
|
|
42
|
+
|
|
43
|
+
**Cause**: Module metadata persists between tests.
|
|
44
|
+
|
|
45
|
+
**Solution**: Clear module metadata in `beforeEach`:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { MODULE_METADATA_KEY } from "../../src/di/module";
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
Reflect.deleteMetadata(MODULE_METADATA_KEY, YourModule);
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Port Already in Use
|
|
56
|
+
|
|
57
|
+
**Error**: `Error: listen EADDRINUSE: address already in use`
|
|
58
|
+
|
|
59
|
+
**Cause**: Another process is using the port, or a previous test didn't clean
|
|
60
|
+
up.
|
|
61
|
+
|
|
62
|
+
**Solution**: Use `getTestPort()` utility in tests:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { getTestPort } from "../utils/test-port";
|
|
66
|
+
|
|
67
|
+
const port = getTestPort();
|
|
68
|
+
const app = new Application({ port });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Module Registration
|
|
72
|
+
|
|
73
|
+
### Module Order Matters
|
|
74
|
+
|
|
75
|
+
Modules should be registered in dependency order. If `AppModule` depends on
|
|
76
|
+
`ConfigModule`, register `ConfigModule` first:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
ConfigModule.forRoot({ defaultConfig: {} });
|
|
80
|
+
CacheModule.forRoot({ defaultTtl: 60000 });
|
|
81
|
+
const app = new Application({ port: 3000 });
|
|
82
|
+
app.registerModule(ConfigModule);
|
|
83
|
+
app.registerModule(CacheModule);
|
|
84
|
+
app.registerModule(AppModule);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Circular Dependencies
|
|
88
|
+
|
|
89
|
+
**Error**: Circular dependency detected or infinite loop during module
|
|
90
|
+
initialization.
|
|
91
|
+
|
|
92
|
+
**Solution**: Refactor modules to remove circular dependencies, or use lazy
|
|
93
|
+
loading:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Use dynamic import for circular dependencies
|
|
97
|
+
const { SomeService } = await import("./some-service");
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Dependency Injection
|
|
101
|
+
|
|
102
|
+
### Service Not Injectable
|
|
103
|
+
|
|
104
|
+
**Error**: `Cannot resolve dependency` or service not found.
|
|
105
|
+
|
|
106
|
+
**Solution**: Ensure the service is decorated with `@Injectable()`:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
@Injectable()
|
|
110
|
+
class UserService {
|
|
111
|
+
// ...
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Token Not Found
|
|
116
|
+
|
|
117
|
+
**Error**: `Provider not found for token`
|
|
118
|
+
|
|
119
|
+
**Solution**: Ensure the token is exported from the module and the module is
|
|
120
|
+
registered:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// In module
|
|
124
|
+
@Module({
|
|
125
|
+
providers: [
|
|
126
|
+
{ provide: MY_TOKEN, useValue: myService },
|
|
127
|
+
],
|
|
128
|
+
exports: [MY_TOKEN],
|
|
129
|
+
})
|
|
130
|
+
class MyModule {}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Routing Issues
|
|
134
|
+
|
|
135
|
+
### Route Not Found (404)
|
|
136
|
+
|
|
137
|
+
**Possible Causes**:
|
|
138
|
+
|
|
139
|
+
1. Controller not registered
|
|
140
|
+
2. Route path mismatch
|
|
141
|
+
3. HTTP method mismatch
|
|
142
|
+
|
|
143
|
+
**Solution**:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Ensure controller is registered
|
|
147
|
+
@Module({
|
|
148
|
+
controllers: [UserController],
|
|
149
|
+
})
|
|
150
|
+
class AppModule {}
|
|
151
|
+
|
|
152
|
+
// Check route path
|
|
153
|
+
@Controller("/api/users")
|
|
154
|
+
class UserController {
|
|
155
|
+
@GET("/:id") // Matches /api/users/:id
|
|
156
|
+
public getUser(@Param("id") id: string) {}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Parameter Not Bound
|
|
161
|
+
|
|
162
|
+
**Error**: `@Param('id')` returns `undefined`
|
|
163
|
+
|
|
164
|
+
**Solution**: Ensure the parameter name matches the route parameter:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// Route: /api/users/:id
|
|
168
|
+
@GET('/:id')
|
|
169
|
+
public getUser(@Param('id') id: string) { // ✅ Correct
|
|
170
|
+
// ...
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@GET('/:id')
|
|
174
|
+
public getUser(@Param('userId') userId: string) { // ❌ Wrong
|
|
175
|
+
// ...
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Performance Issues
|
|
180
|
+
|
|
181
|
+
### Slow Request Handling
|
|
182
|
+
|
|
183
|
+
**Possible Causes**:
|
|
184
|
+
|
|
185
|
+
1. Heavy computation in request handler
|
|
186
|
+
2. Synchronous blocking operations
|
|
187
|
+
3. Large response payloads
|
|
188
|
+
|
|
189
|
+
**Solution**:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Use async/await for I/O operations
|
|
193
|
+
@GET('/users')
|
|
194
|
+
public async getUsers() {
|
|
195
|
+
return await this.userService.findAll(); // ✅ Async
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Avoid blocking operations
|
|
199
|
+
@GET('/users')
|
|
200
|
+
public getUsers() {
|
|
201
|
+
return this.userService.findAllSync(); // ❌ Blocking
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Memory Leaks
|
|
206
|
+
|
|
207
|
+
**Symptoms**: Memory usage increases over time.
|
|
208
|
+
|
|
209
|
+
**Possible Causes**:
|
|
210
|
+
|
|
211
|
+
1. Event listeners not cleaned up
|
|
212
|
+
2. Timers/intervals not cleared
|
|
213
|
+
3. Cache not expiring
|
|
214
|
+
|
|
215
|
+
**Solution**:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Clean up in afterEach/afterAll
|
|
219
|
+
afterEach(async () => {
|
|
220
|
+
await app.stop();
|
|
221
|
+
ModuleRegistry.getInstance().clear();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Use TTL for cache
|
|
225
|
+
CacheModule.forRoot({
|
|
226
|
+
defaultTtl: 3600000, // 1 hour
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Debugging Tips
|
|
231
|
+
|
|
232
|
+
### Enable Debug Logging
|
|
233
|
+
|
|
234
|
+
Set log level to `DEBUG`:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
LoggerModule.forRoot({
|
|
238
|
+
logger: {
|
|
239
|
+
level: LogLevel.DEBUG,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Check Module Metadata
|
|
245
|
+
|
|
246
|
+
Inspect module metadata:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { MODULE_METADATA_KEY } from "../../src/di/module";
|
|
250
|
+
|
|
251
|
+
const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, YourModule);
|
|
252
|
+
console.log("Module metadata:", metadata);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Verify Container State
|
|
256
|
+
|
|
257
|
+
Check what's registered in the container:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const container = app.getContainer();
|
|
261
|
+
// Inspect container internals (implementation-dependent)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Use Test Utilities
|
|
265
|
+
|
|
266
|
+
Use test utilities for consistent test setup:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { getTestPort } from "../utils/test-port";
|
|
270
|
+
|
|
271
|
+
const port = getTestPort();
|
|
272
|
+
const app = new Application({ port });
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Getting Help
|
|
276
|
+
|
|
277
|
+
If you're still experiencing issues:
|
|
278
|
+
|
|
279
|
+
1. Check the [API Documentation](./api.md)
|
|
280
|
+
2. Review [Examples](../examples/)
|
|
281
|
+
3. Search existing [GitHub Issues](https://github.com/your-repo/issues)
|
|
282
|
+
4. Create a new issue with:
|
|
283
|
+
- Bun version
|
|
284
|
+
- Framework version
|
|
285
|
+
- Minimal reproduction code
|
|
286
|
+
- Error messages and stack traces
|