You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: add caveats for proxy provider bootstrap timing and REQUEST-scoped mixing
- Warn that proxy providers are unavailable until onApplicationBootstrap
(BullMQ and similar workers must not consume in onModuleInit)
- Danger block: never inject Scope.REQUEST providers into proxy provider
factories; shows before/after with CLS_REQ pattern
Closes#404, relates to #375
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/docs/03_features-and-use-cases/04_usage-outside-of-web-request.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,3 +43,9 @@ export class CronController {
43
43
Special care must be taken in case you're using [Proxy Providers](../03_features-and-use-cases/06_proxy-providers.md#outside-web-request).
44
44
45
45
:::
46
+
47
+
:::warning
48
+
49
+
If you are using Proxy Providers in a background worker (e.g. BullMQ), make sure the worker does not start consuming jobs in `onModuleInit`. Proxy Providers are only available after `onApplicationBootstrap` completes. See the [relevant caveat](./06_proxy-providers.md#proxy-providers-require-full-application-bootstrap) in the Proxy Providers documentation.
Copy file name to clipboardExpand all lines: docs/docs/03_features-and-use-cases/06_proxy-providers.md
+63Lines changed: 63 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -310,6 +310,69 @@ ClsModule.forFeatureAsync({
310
310
311
311
## Caveats
312
312
313
+
### Proxy Providers require full application bootstrap
314
+
315
+
Proxy providers (and plugins) are registered during the `onApplicationBootstrap` lifecycle hook. Any code that attempts to access a Proxy Provider _before_ this hook completes will encounter an unresolved proxy and may throw a `ProxyProviderNotResolvedException`.
316
+
317
+
It goes without saying that any access to any property on a Proxy provider **in the constructor** will always evaluate to `undefined` (or throw in strict mode).
318
+
319
+
:::warning
320
+
321
+
This is a common pitfall with background job processors such as **BullMQ workers**, which start consuming messages in `onModuleInit` — before `onApplicationBootstrap` has run. Accessing a Proxy Provider from within a job handler at that point will fail.
322
+
323
+
To avoid this, ensure the application is fully bootstrapped before your worker begins consuming. Either start consuming in `onApplicationBootstrap` instead of `onModuleInit`, or delay consumption until after `app.listen()` / `app.init()` resolves:
// Use onApplicationBootstrap instead of onModuleInit
331
+
// to ensure Proxy Providers are available.
332
+
onApplicationBootstrap() {
333
+
this.worker.run();
334
+
}
335
+
}
336
+
```
337
+
338
+
:::
339
+
340
+
### Do not mix REQUEST-scoped providers with Proxy Providers
341
+
342
+
:::danger
343
+
344
+
Never inject a real NestJS `Scope.REQUEST` (or `durable: true`) provider as a dependency of a Proxy Provider or any `ClsModule` plugin.
345
+
346
+
:::
347
+
348
+
Proxy Providers are **singletons** from NestJS's DI perspective. When NestJS detects that a singleton depends on a REQUEST-scoped provider, it changes the scope of the singleton to REQUEST as well. This means the Proxy wrapper itself is re-created on every request, which defeats the purpose of Proxy Providers and can cause **cross-request contamination** (e.g. tenant connections leaking between requests).
349
+
350
+
```ts
351
+
// ❌ Wrong: TENANT_CONNECTION depends on a real Scope.REQUEST provider
`CLS_REQ` is itself a Proxy Provider (a singleton that delegates to the per-request value stored in CLS), so the factory above remains a singleton from NestJS's point of view while still resolving the correct request on each access.
375
+
313
376
### No primitive values
314
377
315
378
Proxy Factory providers _cannot_ return a _primitive value_ (`string`, `number`, `boolean`, `null`, or `undefined`). Doing so throws a `ProxyProviderInvalidReturnTypeException` at resolution time. This is because the provider itself is the Proxy and it only delegates access once a property or a method is called on it (or if it itself is called in case the factory returns a function).
0 commit comments