@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.
@@ -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