@navios/di 0.3.1 → 0.4.1

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.
Files changed (97) hide show
  1. package/README.md +67 -6
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +2659 -0
  5. package/coverage/coverage-final.json +46 -0
  6. package/coverage/docs/examples/basic-usage.mts.html +376 -0
  7. package/coverage/docs/examples/factory-pattern.mts.html +1039 -0
  8. package/coverage/docs/examples/index.html +176 -0
  9. package/coverage/docs/examples/injection-tokens.mts.html +760 -0
  10. package/coverage/docs/examples/request-scope-example.mts.html +847 -0
  11. package/coverage/docs/examples/service-lifecycle.mts.html +1162 -0
  12. package/coverage/favicon.png +0 -0
  13. package/coverage/index.html +236 -0
  14. package/coverage/lib/_tsup-dts-rollup.d.mts.html +2806 -0
  15. package/coverage/lib/index.d.mts.html +310 -0
  16. package/coverage/lib/index.html +131 -0
  17. package/coverage/prettify.css +1 -0
  18. package/coverage/prettify.js +2 -0
  19. package/coverage/sort-arrow-sprite.png +0 -0
  20. package/coverage/sorter.js +196 -0
  21. package/coverage/src/container.mts.html +586 -0
  22. package/coverage/src/decorators/factory.decorator.mts.html +322 -0
  23. package/coverage/src/decorators/index.html +146 -0
  24. package/coverage/src/decorators/index.mts.html +91 -0
  25. package/coverage/src/decorators/injectable.decorator.mts.html +394 -0
  26. package/coverage/src/enums/index.html +146 -0
  27. package/coverage/src/enums/index.mts.html +91 -0
  28. package/coverage/src/enums/injectable-scope.enum.mts.html +127 -0
  29. package/coverage/src/enums/injectable-type.enum.mts.html +97 -0
  30. package/coverage/src/errors/errors.enum.mts.html +109 -0
  31. package/coverage/src/errors/factory-not-found.mts.html +109 -0
  32. package/coverage/src/errors/factory-token-not-resolved.mts.html +115 -0
  33. package/coverage/src/errors/index.html +221 -0
  34. package/coverage/src/errors/index.mts.html +106 -0
  35. package/coverage/src/errors/instance-destroying.mts.html +109 -0
  36. package/coverage/src/errors/instance-expired.mts.html +109 -0
  37. package/coverage/src/errors/instance-not-found.mts.html +109 -0
  38. package/coverage/src/errors/unknown-error.mts.html +130 -0
  39. package/coverage/src/event-emitter.mts.html +400 -0
  40. package/coverage/src/factory-context.mts.html +109 -0
  41. package/coverage/src/index.html +296 -0
  42. package/coverage/src/index.mts.html +139 -0
  43. package/coverage/src/injection-token.mts.html +571 -0
  44. package/coverage/src/injector.mts.html +133 -0
  45. package/coverage/src/interfaces/factory.interface.mts.html +121 -0
  46. package/coverage/src/interfaces/index.html +161 -0
  47. package/coverage/src/interfaces/index.mts.html +94 -0
  48. package/coverage/src/interfaces/on-service-destroy.interface.mts.html +94 -0
  49. package/coverage/src/interfaces/on-service-init.interface.mts.html +94 -0
  50. package/coverage/src/registry.mts.html +247 -0
  51. package/coverage/src/request-context-holder.mts.html +607 -0
  52. package/coverage/src/service-instantiator.mts.html +559 -0
  53. package/coverage/src/service-locator-event-bus.mts.html +289 -0
  54. package/coverage/src/service-locator-instance-holder.mts.html +307 -0
  55. package/coverage/src/service-locator-manager.mts.html +604 -0
  56. package/coverage/src/service-locator.mts.html +2911 -0
  57. package/coverage/src/symbols/index.html +131 -0
  58. package/coverage/src/symbols/index.mts.html +88 -0
  59. package/coverage/src/symbols/injectable-token.mts.html +88 -0
  60. package/coverage/src/utils/defer.mts.html +304 -0
  61. package/coverage/src/utils/get-injectable-token.mts.html +142 -0
  62. package/coverage/src/utils/get-injectors.mts.html +691 -0
  63. package/coverage/src/utils/index.html +176 -0
  64. package/coverage/src/utils/index.mts.html +97 -0
  65. package/coverage/src/utils/types.mts.html +241 -0
  66. package/docs/README.md +5 -2
  67. package/docs/api-reference.md +38 -0
  68. package/docs/container.md +75 -0
  69. package/docs/getting-started.md +4 -3
  70. package/docs/injectable.md +4 -3
  71. package/docs/migration.md +177 -0
  72. package/docs/request-contexts.md +364 -0
  73. package/lib/_tsup-dts-rollup.d.mts +182 -41
  74. package/lib/_tsup-dts-rollup.d.ts +182 -41
  75. package/lib/index.d.mts +1 -0
  76. package/lib/index.d.ts +1 -0
  77. package/lib/index.js +480 -294
  78. package/lib/index.js.map +1 -1
  79. package/lib/index.mjs +480 -295
  80. package/lib/index.mjs.map +1 -1
  81. package/package.json +1 -1
  82. package/src/__tests__/defer.spec.mts +166 -0
  83. package/src/__tests__/errors.spec.mts +61 -0
  84. package/src/__tests__/event-emitter.spec.mts +163 -0
  85. package/src/__tests__/get-injectors.spec.mts +70 -0
  86. package/src/__tests__/registry.spec.mts +335 -0
  87. package/src/__tests__/request-scope.spec.mts +34 -35
  88. package/src/__tests__/service-instantiator.spec.mts +408 -0
  89. package/src/__tests__/service-locator-event-bus.spec.mts +242 -0
  90. package/src/__tests__/service-locator-manager.spec.mts +370 -0
  91. package/src/__tests__/unified-api.spec.mts +130 -0
  92. package/src/base-instance-holder-manager.mts +175 -0
  93. package/src/event-emitter.mts +5 -5
  94. package/src/index.mts +1 -0
  95. package/src/request-context-holder.mts +73 -44
  96. package/src/service-locator-manager.mts +12 -70
  97. package/src/service-locator.mts +421 -226
package/docs/container.md CHANGED
@@ -143,6 +143,41 @@ const serviceLocator = container.getServiceLocator()
143
143
  const instance = await serviceLocator.getOrThrowInstance(UserService)
144
144
  ```
145
145
 
146
+ ## Request Context Management
147
+
148
+ The Container provides built-in support for request contexts, allowing you to manage request-scoped services:
149
+
150
+ ```typescript
151
+ const container = new Container()
152
+
153
+ // Begin a request context
154
+ const context = container.beginRequest('req-123', { userId: 456 }, 100)
155
+
156
+ // Add request-specific instances
157
+ const REQUEST_ID_TOKEN = InjectionToken.create<string>('REQUEST_ID')
158
+ context.addInstance(REQUEST_ID_TOKEN, 'req-123')
159
+
160
+ // Set as current context
161
+ container.setCurrentRequestContext('req-123')
162
+
163
+ // Services can now access request-scoped data
164
+ @Injectable()
165
+ class RequestService {
166
+ private readonly requestId = asyncInject(REQUEST_ID_TOKEN)
167
+
168
+ async process() {
169
+ const id = await this.requestId
170
+ console.log(`Processing in request: ${id}`)
171
+ }
172
+ }
173
+
174
+ const service = await container.get(RequestService)
175
+ await service.process() // "Processing in request: req-123"
176
+
177
+ // Clean up when done
178
+ await container.endRequest('req-123')
179
+ ```
180
+
146
181
  ## Best Practices
147
182
 
148
183
  ### 1. Use Container for Application Setup
@@ -215,6 +250,26 @@ const userContainer = new Container(userRegistry)
215
250
  const paymentContainer = new Container(paymentRegistry)
216
251
  ```
217
252
 
253
+ ### 5. Manage Request Contexts Properly
254
+
255
+ ```typescript
256
+ // Always clean up request contexts
257
+ async function handleRequest(req, res) {
258
+ const requestId = generateRequestId()
259
+ const context = container.beginRequest(requestId, {
260
+ ip: req.ip,
261
+ userAgent: req.get('User-Agent'),
262
+ })
263
+
264
+ try {
265
+ container.setCurrentRequestContext(requestId)
266
+ await processRequest(req, res)
267
+ } finally {
268
+ await container.endRequest(requestId)
269
+ }
270
+ }
271
+ ```
272
+
218
273
  ## API Reference
219
274
 
220
275
  ### Constructor
@@ -252,6 +307,26 @@ Waits for all pending operations to complete.
252
307
 
253
308
  Returns the underlying ServiceLocator instance.
254
309
 
310
+ #### `beginRequest(requestId: string, metadata?: Record<string, any>, priority?: number): RequestContextHolder`
311
+
312
+ Begins a new request context with the given parameters.
313
+
314
+ - `requestId`: Unique identifier for this request
315
+ - `metadata`: Optional metadata for the request
316
+ - `priority`: Priority for resolution (higher = more priority, defaults to 100)
317
+
318
+ #### `endRequest(requestId: string): Promise<void>`
319
+
320
+ Ends a request context and cleans up all associated instances.
321
+
322
+ #### `getCurrentRequestContext(): RequestContextHolder | null`
323
+
324
+ Gets the current request context.
325
+
326
+ #### `setCurrentRequestContext(requestId: string): void`
327
+
328
+ Sets the current request context.
329
+
255
330
  ## Error Handling
256
331
 
257
332
  The Container can throw various errors:
@@ -66,13 +66,14 @@ class EmailService {
66
66
  // 2. Create a user service that depends on the email service
67
67
  @Injectable()
68
68
  class UserService {
69
- private readonly emailService = inject(EmailService)
69
+ private readonly emailService = asyncInject(EmailService)
70
70
 
71
71
  async createUser(name: string, email: string) {
72
72
  console.log(`Creating user: ${name}`)
73
73
 
74
- // Send welcome email
75
- await this.emailService.sendEmail(
74
+ // Get the email service and send welcome email
75
+ const emailService = await this.emailService
76
+ await emailService.sendEmail(
76
77
  email,
77
78
  'Welcome!',
78
79
  `Hello ${name}, welcome to our platform!`,
@@ -20,7 +20,7 @@ class UserService {
20
20
  ### Service with Dependencies
21
21
 
22
22
  ```typescript
23
- import { inject, Injectable } from '@navios/di'
23
+ import { asyncInject, Injectable } from '@navios/di'
24
24
 
25
25
  @Injectable()
26
26
  class DatabaseService {
@@ -31,10 +31,11 @@ class DatabaseService {
31
31
 
32
32
  @Injectable()
33
33
  class UserService {
34
- private readonly db = inject(DatabaseService)
34
+ private readonly db = asyncInject(DatabaseService)
35
35
 
36
36
  async getUsers() {
37
- const connection = await this.db.connect()
37
+ const dbService = await this.db
38
+ const connection = await dbService.connect()
38
39
  return `Users from ${connection}`
39
40
  }
40
41
  }
@@ -0,0 +1,177 @@
1
+ # Migration Guide
2
+
3
+ This guide helps you migrate between different versions of Navios DI.
4
+
5
+ ## Migrating to v0.3.x
6
+
7
+ ### New Features
8
+
9
+ #### Request Context Management
10
+
11
+ The biggest addition in v0.3.x is request context management. This feature allows you to manage request-scoped services with automatic cleanup.
12
+
13
+ **New APIs:**
14
+
15
+ - `Container.beginRequest(requestId, metadata?, priority?)`
16
+ - `Container.endRequest(requestId)`
17
+ - `Container.getCurrentRequestContext()`
18
+ - `Container.setCurrentRequestContext(requestId)`
19
+ - `RequestContextHolder` interface
20
+
21
+ **Example:**
22
+
23
+ ```typescript
24
+ // New request context API
25
+ const container = new Container()
26
+ const context = container.beginRequest('req-123', { userId: 456 })
27
+ container.setCurrentRequestContext('req-123')
28
+
29
+ // Add request-scoped data
30
+ const REQUEST_ID_TOKEN = InjectionToken.create<string>('REQUEST_ID')
31
+ context.addInstance(REQUEST_ID_TOKEN, 'req-123')
32
+
33
+ // Use in services
34
+ @Injectable()
35
+ class RequestService {
36
+ private readonly requestId = asyncInject(REQUEST_ID_TOKEN)
37
+
38
+ async process() {
39
+ const id = await this.requestId
40
+ console.log(`Processing request: ${id}`)
41
+ }
42
+ }
43
+
44
+ // Clean up when done
45
+ await container.endRequest('req-123')
46
+ ```
47
+
48
+ ### Recommended Changes
49
+
50
+ #### Prefer `asyncInject` over `inject`
51
+
52
+ While `inject` still works, `asyncInject` is now the recommended approach for most use cases as it's safer and handles async dependencies better.
53
+
54
+ **Before:**
55
+
56
+ ```typescript
57
+ @Injectable()
58
+ class UserService {
59
+ private readonly db = inject(DatabaseService)
60
+ }
61
+ ```
62
+
63
+ **After:**
64
+
65
+ ```typescript
66
+ @Injectable()
67
+ class UserService {
68
+ private readonly db = asyncInject(DatabaseService)
69
+
70
+ async getUsers() {
71
+ const database = await this.db
72
+ return database.query('SELECT * FROM users')
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### Breaking Changes
78
+
79
+ None in v0.3.x - this is a feature release with full backward compatibility.
80
+
81
+ ## Migrating from v0.2.x to v0.3.x
82
+
83
+ ### New Dependencies
84
+
85
+ No new peer dependencies were added.
86
+
87
+ ### Updated Type Definitions
88
+
89
+ Some internal type definitions were improved for better TypeScript support, but no breaking changes to public APIs.
90
+
91
+ ## Future Migration Notes
92
+
93
+ ### Planned for v0.4.x
94
+
95
+ - Enhanced request context lifecycle hooks
96
+ - Performance optimizations for high-throughput scenarios
97
+ - Additional factory pattern improvements
98
+
99
+ ### Best Practices for Forward Compatibility
100
+
101
+ 1. **Use `asyncInject` for new code** - This is the future-preferred injection method
102
+ 2. **Implement lifecycle hooks** - `OnServiceInit` and `OnServiceDestroy` for proper resource management
103
+ 3. **Use request contexts** - For web applications and request-scoped data
104
+ 4. **Prefer injection tokens** - For configuration and interface-based dependencies
105
+
106
+ ## Common Migration Issues
107
+
108
+ ### Issue: Services not found in request context
109
+
110
+ **Problem:** Services can't be resolved when using request contexts.
111
+
112
+ **Solution:** Make sure you've set the current request context:
113
+
114
+ ```typescript
115
+ container.setCurrentRequestContext('your-request-id')
116
+ ```
117
+
118
+ ### Issue: Async dependencies not ready
119
+
120
+ **Problem:** Using `inject` with dependencies that aren't immediately available.
121
+
122
+ **Solution:** Switch to `asyncInject`:
123
+
124
+ ```typescript
125
+ // Instead of this:
126
+ private readonly service = inject(AsyncService)
127
+
128
+ // Use this:
129
+ private readonly service = asyncInject(AsyncService)
130
+ ```
131
+
132
+ ### Issue: Memory leaks with request contexts
133
+
134
+ **Problem:** Request contexts not being cleaned up.
135
+
136
+ **Solution:** Always call `endRequest()`:
137
+
138
+ ```typescript
139
+ try {
140
+ const context = container.beginRequest('req-123')
141
+ // ... process request
142
+ } finally {
143
+ await container.endRequest('req-123')
144
+ }
145
+ ```
146
+
147
+ ## Getting Help
148
+
149
+ If you encounter issues during migration:
150
+
151
+ 1. Check the [API Reference](./api-reference.md) for detailed method signatures
152
+ 2. Review the [examples](./examples/) for common patterns
153
+ 3. Check the GitHub issues for known migration problems
154
+ 4. Create a new issue if you find a bug or need help
155
+
156
+ ## Changelog Summary
157
+
158
+ ### v0.3.1
159
+
160
+ - Added request context management
161
+ - Improved TypeScript type definitions
162
+ - Enhanced container API with request context methods
163
+ - New `RequestContextHolder` interface
164
+ - Better error messages for injection failures
165
+
166
+ ### v0.3.0
167
+
168
+ - Initial release with request context support
169
+ - Container API improvements
170
+ - Better async injection handling
171
+
172
+ ### v0.2.x
173
+
174
+ - Basic dependency injection functionality
175
+ - Injectable and Factory decorators
176
+ - Injection tokens with Zod validation
177
+ - Service lifecycle hooks
@@ -0,0 +1,364 @@
1
+ # Request Contexts
2
+
3
+ Request contexts in Navios DI provide a powerful way to manage request-scoped services with automatic cleanup and priority-based resolution. This is particularly useful in web applications where you need to maintain request-specific data and ensure proper cleanup after each request.
4
+
5
+ ## Overview
6
+
7
+ A request context is a scoped container that can hold pre-prepared instances and metadata for a specific request. When a request context is active, services can access request-specific data through dependency injection.
8
+
9
+ ### Key Features
10
+
11
+ - **Request-scoped instances**: Services that exist only for the duration of a request
12
+ - **Automatic cleanup**: All request-scoped instances are automatically cleaned up when the request ends
13
+ - **Priority-based resolution**: Multiple contexts can exist with different priorities
14
+ - **Metadata support**: Attach arbitrary metadata to request contexts
15
+ - **Thread-safe**: Safe to use in concurrent environments
16
+
17
+ ## Basic Usage
18
+
19
+ ### Creating and Managing Request Contexts
20
+
21
+ ```typescript
22
+ import { Container, Injectable, InjectionToken } from '@navios/di'
23
+
24
+ const container = new Container()
25
+
26
+ // Begin a new request context
27
+ const context = container.beginRequest('req-123', { userId: 456 }, 100)
28
+
29
+ // Set it as the current context
30
+ container.setCurrentRequestContext('req-123')
31
+
32
+ // End the request context when done
33
+ await container.endRequest('req-123')
34
+ ```
35
+
36
+ ### Using Request-Scoped Data
37
+
38
+ ```typescript
39
+ const REQUEST_ID_TOKEN = InjectionToken.create<string>('REQUEST_ID')
40
+ const USER_ID_TOKEN = InjectionToken.create<number>('USER_ID')
41
+
42
+ @Injectable()
43
+ class RequestLogger {
44
+ private readonly requestId = asyncInject(REQUEST_ID_TOKEN)
45
+ private readonly userId = asyncInject(USER_ID_TOKEN)
46
+
47
+ async log(message: string) {
48
+ const reqId = await this.requestId
49
+ const uid = await this.userId
50
+ console.log(`[${reqId}] User ${uid}: ${message}`)
51
+ }
52
+ }
53
+
54
+ // Setup request context
55
+ const context = container.beginRequest('req-123')
56
+ context.addInstance(REQUEST_ID_TOKEN, 'req-123')
57
+ context.addInstance(USER_ID_TOKEN, 456)
58
+
59
+ container.setCurrentRequestContext('req-123')
60
+
61
+ // Use the service
62
+ const logger = await container.get(RequestLogger)
63
+ await logger.log('Processing request') // "[req-123] User 456: Processing request"
64
+ ```
65
+
66
+ ## Advanced Features
67
+
68
+ ### Priority-Based Resolution
69
+
70
+ When multiple request contexts exist, the one with the highest priority is used:
71
+
72
+ ```typescript
73
+ // High priority context (e.g., admin request)
74
+ const adminContext = container.beginRequest('admin-req', {}, 200)
75
+
76
+ // Normal priority context
77
+ const userContext = container.beginRequest('user-req', {}, 100)
78
+
79
+ // Admin context will be used due to higher priority
80
+ container.setCurrentRequestContext('admin-req')
81
+ ```
82
+
83
+ ### Request Metadata
84
+
85
+ Request contexts can carry metadata that services can access:
86
+
87
+ ```typescript
88
+ @Injectable()
89
+ class AuditService {
90
+ private readonly context = asyncInject(Container)
91
+
92
+ async logAction(action: string) {
93
+ const container = await this.context
94
+ const requestContext = container.getCurrentRequestContext()
95
+
96
+ if (requestContext) {
97
+ const userId = requestContext.getMetadata('userId')
98
+ const traceId = requestContext.getMetadata('traceId')
99
+
100
+ console.log(`User ${userId} performed ${action} (trace: ${traceId})`)
101
+ }
102
+ }
103
+ }
104
+
105
+ // Setup with metadata
106
+ const context = container.beginRequest('req-123', {
107
+ userId: 456,
108
+ traceId: 'abc-123',
109
+ userAgent: 'Mozilla/5.0...',
110
+ })
111
+ ```
112
+
113
+ ### Pre-prepared Instances
114
+
115
+ You can add pre-prepared instances to a request context:
116
+
117
+ ```typescript
118
+ @Injectable()
119
+ class DatabaseConnection {
120
+ constructor(private connectionString: string) {}
121
+
122
+ async query(sql: string) {
123
+ return `Executing: ${sql} on ${this.connectionString}`
124
+ }
125
+ }
126
+
127
+ // Create a request-specific database connection
128
+ const dbConnection = new DatabaseConnection('user-specific-db')
129
+ const context = container.beginRequest('req-123')
130
+ context.addInstance('DatabaseConnection', dbConnection)
131
+ ```
132
+
133
+ ## Web Framework Integration
134
+
135
+ ### Express.js Example
136
+
137
+ ```typescript
138
+ import { Container, Injectable, InjectionToken } from '@navios/di'
139
+
140
+ import express from 'express'
141
+
142
+ const REQUEST_TOKEN = InjectionToken.create<express.Request>('REQUEST')
143
+ const RESPONSE_TOKEN = InjectionToken.create<express.Response>('RESPONSE')
144
+
145
+ @Injectable()
146
+ class RequestHandler {
147
+ private readonly req = asyncInject(REQUEST_TOKEN)
148
+ private readonly res = asyncInject(RESPONSE_TOKEN)
149
+
150
+ async handleRequest() {
151
+ const request = await this.req
152
+ const response = await this.res
153
+
154
+ response.json({
155
+ message: 'Hello!',
156
+ path: request.path,
157
+ method: request.method,
158
+ })
159
+ }
160
+ }
161
+
162
+ const app = express()
163
+ const container = new Container()
164
+
165
+ app.use('*', async (req, res, next) => {
166
+ const requestId = `req-${Date.now()}-${Math.random()}`
167
+
168
+ // Create request context
169
+ const context = container.beginRequest(requestId, {
170
+ path: req.path,
171
+ method: req.method,
172
+ userAgent: req.get('User-Agent'),
173
+ })
174
+
175
+ // Add request-specific instances
176
+ context.addInstance(REQUEST_TOKEN, req)
177
+ context.addInstance(RESPONSE_TOKEN, res)
178
+
179
+ // Set as current context
180
+ container.setCurrentRequestContext(requestId)
181
+
182
+ try {
183
+ const handler = await container.get(RequestHandler)
184
+ await handler.handleRequest()
185
+ } finally {
186
+ // Clean up request context
187
+ await container.endRequest(requestId)
188
+ }
189
+ })
190
+ ```
191
+
192
+ ### Fastify Example
193
+
194
+ ```typescript
195
+ import { Container, Injectable, InjectionToken } from '@navios/di'
196
+
197
+ import fastify from 'fastify'
198
+
199
+ const REQUEST_TOKEN = InjectionToken.create<any>('FASTIFY_REQUEST')
200
+
201
+ const app = fastify()
202
+ const container = new Container()
203
+
204
+ app.addHook('preHandler', async (request, reply) => {
205
+ const requestId = `req-${request.id}`
206
+
207
+ const context = container.beginRequest(requestId, {
208
+ ip: request.ip,
209
+ userAgent: request.headers['user-agent'],
210
+ })
211
+
212
+ context.addInstance(REQUEST_TOKEN, request)
213
+ container.setCurrentRequestContext(requestId)
214
+
215
+ // Store requestId for cleanup
216
+ request.requestId = requestId
217
+ })
218
+
219
+ app.addHook('onResponse', async (request, reply) => {
220
+ if (request.requestId) {
221
+ await container.endRequest(request.requestId)
222
+ }
223
+ })
224
+ ```
225
+
226
+ ## Best Practices
227
+
228
+ ### 1. Always Clean Up
229
+
230
+ Always ensure request contexts are properly cleaned up:
231
+
232
+ ```typescript
233
+ const requestId = generateRequestId()
234
+ const context = container.beginRequest(requestId)
235
+
236
+ try {
237
+ // Process request
238
+ await processRequest()
239
+ } finally {
240
+ // Always clean up, even on errors
241
+ await container.endRequest(requestId)
242
+ }
243
+ ```
244
+
245
+ ### 2. Use Meaningful Request IDs
246
+
247
+ Use descriptive request IDs that help with debugging:
248
+
249
+ ```typescript
250
+ const requestId = `${req.method}-${req.path}-${Date.now()}-${Math.random().toString(36).slice(2)}`
251
+ ```
252
+
253
+ ### 3. Set Appropriate Priorities
254
+
255
+ Use priorities to ensure correct resolution order:
256
+
257
+ ```typescript
258
+ // System/admin requests - highest priority
259
+ const adminContext = container.beginRequest('admin-req', {}, 1000)
260
+
261
+ // Authenticated user requests
262
+ const userContext = container.beginRequest('user-req', {}, 500)
263
+
264
+ // Anonymous requests - lowest priority
265
+ const anonContext = container.beginRequest('anon-req', {}, 100)
266
+ ```
267
+
268
+ ### 4. Leverage Metadata
269
+
270
+ Use metadata for cross-cutting concerns:
271
+
272
+ ```typescript
273
+ const context = container.beginRequest('req-123', {
274
+ traceId: generateTraceId(),
275
+ correlationId: req.headers['x-correlation-id'],
276
+ userId: req.user?.id,
277
+ tenantId: req.tenant?.id,
278
+ startTime: Date.now(),
279
+ })
280
+ ```
281
+
282
+ ### 5. Combine with Lifecycle Hooks
283
+
284
+ Use lifecycle hooks for request-scoped resource management:
285
+
286
+ ```typescript
287
+ @Injectable()
288
+ class DatabaseTransaction implements OnServiceInit, OnServiceDestroy {
289
+ private transaction: any = null
290
+
291
+ async onServiceInit() {
292
+ this.transaction = await db.beginTransaction()
293
+ }
294
+
295
+ async onServiceDestroy() {
296
+ if (this.transaction) {
297
+ await this.transaction.rollback()
298
+ }
299
+ }
300
+
301
+ async commit() {
302
+ await this.transaction.commit()
303
+ }
304
+ }
305
+ ```
306
+
307
+ ## Error Handling
308
+
309
+ Request contexts handle errors gracefully:
310
+
311
+ ```typescript
312
+ try {
313
+ const context = container.beginRequest('req-123')
314
+ container.setCurrentRequestContext('req-123')
315
+
316
+ // If this throws, cleanup will still happen
317
+ await processRequest()
318
+ } catch (error) {
319
+ console.error('Request failed:', error)
320
+ throw error
321
+ } finally {
322
+ // Context cleanup happens automatically
323
+ await container.endRequest('req-123')
324
+ }
325
+ ```
326
+
327
+ ## API Reference
328
+
329
+ ### Container Methods
330
+
331
+ - `beginRequest(requestId: string, metadata?: Record<string, any>, priority?: number): RequestContextHolder`
332
+ - `endRequest(requestId: string): Promise<void>`
333
+ - `getCurrentRequestContext(): RequestContextHolder | null`
334
+ - `setCurrentRequestContext(requestId: string): void`
335
+
336
+ ### RequestContextHolder Interface
337
+
338
+ - `requestId: string` - Unique identifier for this request
339
+ - `priority: number` - Priority for resolution
340
+ - `metadata: Map<string, any>` - Request-specific metadata
341
+ - `createdAt: number` - Timestamp when context was created
342
+ - `addInstance(token: InjectionToken<any>, instance: any): void`
343
+ - `getMetadata(key: string): any | undefined`
344
+ - `setMetadata(key: string, value: any): void`
345
+ - `clear(): void` - Clear all instances and metadata
346
+
347
+ ## Performance Considerations
348
+
349
+ - Request contexts are lightweight and designed for high-throughput scenarios
350
+ - Cleanup is asynchronous and won't block request processing
351
+ - Use appropriate priorities to avoid unnecessary context switching
352
+ - Consider pooling request contexts for very high-frequency scenarios
353
+
354
+ ## Troubleshooting
355
+
356
+ ### Common Issues
357
+
358
+ **Context not found**: Make sure you've called `setCurrentRequestContext()` before accessing request-scoped services.
359
+
360
+ **Wrong priority resolution**: Check that your priority values are set correctly (higher = more priority).
361
+
362
+ **Memory leaks**: Always call `endRequest()` to clean up contexts, preferably in a `finally` block.
363
+
364
+ **Service not found in context**: Ensure you've added the instance to the context with `addInstance()`.