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
feat: enable strict mode for Proxy Providers (#171)
* test: clean ProxyProviderManager state in forRoot to support tests
* feat: add `strict` option to proxy providers
* feat: enable setting proxy provider `strict` option via a decorator.
* docs: clarify section on Proxy Providers
* docs: add docs on strict proxy providers
Copy file name to clipboardExpand all lines: docs/docs/03_features-and-use-cases/06_proxy-providers.md
+93-33Lines changed: 93 additions & 33 deletions
Original file line number
Diff line number
Diff line change
@@ -147,41 +147,11 @@ class DogsService {
147
147
}
148
148
```
149
149
150
-
## Caveats
151
-
152
-
### No primitive values
153
-
154
-
Proxy Factory providers _cannot_ return a _primitive value_. 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).
155
-
156
-
### `function` Proxies must be explicitly enabled
157
-
158
-
In order to support injecting proxies of _functions_, the underlying proxy _target_ must be a function, too, in order to be able to implement the "apply" trap. However, this information cannot be extracted from the factory function itself, so if your factory returns a function, you must explicitly set the `type` property to `function` in the provider definition.
159
-
160
-
```ts
161
-
{
162
-
provide: SOME_FUNCTION,
163
-
useFactory: () => {
164
-
return () => {
165
-
// do something
166
-
};
167
-
},
168
-
// highlight-start
169
-
type: 'function',
170
-
// highlight-end
171
-
}
172
-
```
173
-
174
-
:::note
175
-
176
-
In versions prior to `v4.0`, calling `typeof` on an instance of a Proxy provider always returned `function`, regardless of the value it holds. This is no longer the case. Please see [Issue #82](https://github.com/Papooch/nestjs-cls/issues/82)
177
-
178
-
:::
179
-
180
150
## Delayed resolution of Proxy Providers
181
151
182
152
By default, proxy providers are resolved as soon as the `setup` function in an enhancer (middleware/guard/interceptor) finishes. For some use cases, it might be required that the resolution is delayed until some later point in the request lifecycle once more information is present in the CLS .
183
153
184
-
To achieve that, set `resolveProxyProviders` to `false` in the enhancer options and call `ClsService#resolveProxyProviders()` manually at any time.
154
+
To achieve that, set `resolveProxyProviders` to `false` in the enhancer options and call (and await) `ClsService#resolveProxyProviders()` manually at any time.
185
155
186
156
```ts
187
157
ClsModule.forRoot({
@@ -191,15 +161,19 @@ ClsModule.forRoot({
191
161
// highlight-end
192
162
},
193
163
});
164
+
165
+
//... later
166
+
167
+
awaitthis.cls.resolveProxyProviders();
194
168
```
195
169
196
170
### Outside web request
197
171
198
-
This is also necessary [outside the context of web request](./04_usage-outside-of-web-request.md), otherwise all access to an injected Proxy Provider will return `undefined`.
172
+
This might also be necessary [outside the context of web request](./04_usage-outside-of-web-request.md).
199
173
200
174
#### With cls.run()
201
175
202
-
If you set up the context with `cls.run()` to wrap any subsequent code thar relies on Proxy Providers.
176
+
If you set up the context with `cls.run()` to wrap any subsequent code thar relies on Proxy Providers, you _must_ call `ClsService#resolveProxyProviders()` before accessing them, otherwise access to any property of the injected Proxy Provider will return `undefined`, that is because an unresolved Proxy Provider falls back to an _empty object_.
203
177
204
178
```ts title=cron.controller.ts
205
179
@Injectable()
@@ -248,3 +222,89 @@ export class CronController {
248
222
}
249
223
}
250
224
```
225
+
226
+
### Selective resolution of Proxy Providers
227
+
228
+
You can also selectively resolve a subset of Proxy Providers, by passing a list of their injection tokens to `ClsService#resolveProxyProviders(tokens)`. This is useful if the providers need to be resolved in a specific order or when some part of the application does not need all of them.
// resolves the rest of the providers that have not been resolved yet
237
+
awaitthis.cls.resolveProxyProviders();
238
+
```
239
+
240
+
## Strict Proxy Providers
241
+
242
+
<small>since `v4.4.0`</small>
243
+
244
+
By default, accessing an unresolved Proxy Provider behaves as if it was an _empty object_. In order to prevent silent failures, you can set the `strict` option to `true` in the proxy provider registration. In this case, any attempt to access a property or a method on an unresolved Proxy Provider will throw an error.
245
+
246
+
For Class Proxy Providers, you can use the according option on the `@InjectableProxy()` decorator.
247
+
248
+
```ts title=user.proxy.ts
249
+
@InjectableProxy({
250
+
// highlight-start
251
+
strict: true,
252
+
// highlight-end
253
+
})
254
+
exportclassUser {
255
+
id:number;
256
+
role:string;
257
+
}
258
+
```
259
+
260
+
In case of Factory Proxy Providers, use the option on the `ClsModule.forFeatureAsync()` registration.
261
+
262
+
```ts
263
+
ClsModule.forFeatureAsync({
264
+
provide: TENANT_CONNECTION,
265
+
import: [DatabaseConnectionModule],
266
+
inject: [CLS_REQ],
267
+
useFactory: async (req:Request) => {
268
+
// ... some implementation
269
+
},
270
+
// highlight-start
271
+
strict: true,
272
+
// highlight-end
273
+
});
274
+
```
275
+
276
+
## Caveats
277
+
278
+
### No primitive values
279
+
280
+
Proxy Factory providers _cannot_ return a _primitive value_. 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).
281
+
282
+
### `function` Proxies must be explicitly enabled
283
+
284
+
In order to support injecting proxies of _functions_, the underlying proxy _target_ must be a function, too, in order to be able to implement the "apply" trap. However, this information cannot be extracted from the factory function itself, so if your factory returns a function, you must explicitly set the `type` property to `function` in the provider definition.
285
+
286
+
```ts
287
+
{
288
+
provide: SOME_FUNCTION,
289
+
useFactory: () => {
290
+
return () => {
291
+
// do something
292
+
};
293
+
},
294
+
// highlight-start
295
+
type: 'function',
296
+
// highlight-end
297
+
}
298
+
```
299
+
300
+
:::note
301
+
302
+
In versions prior to `v4.0`, calling `typeof` on an instance of a Proxy provider always returned `function`, regardless of the value it holds. This is no longer the case. Please see [Issue #82](https://github.com/Papooch/nestjs-cls/issues/82)
303
+
304
+
:::
305
+
306
+
### Limited support for injecting Proxy Providers into each other
307
+
308
+
Apart from the built-in `CLS_REQ` and `CLS_RES` proxy providers, custom Proxy Providers cannot be _reliably_ injected into other Proxy Providers, because there is no system in place to resolve them in the correct order (as far as Nest is concerned, all of them have already been bootstrapped, so it can't help us here), so it may happen, that during the proxy provider resolution phase, a Proxy Provider that is injected into another Proxy Provider is not yet resolved and falls back to an empty object.
309
+
310
+
There is an open [feature request](https://github.com/Papooch/nestjs-cls/issues/169) to address this shortcoming, but until then, refer to the manual [Selective resolution of Proxy Providers](#selective-resolution-of-proxy-providers) technique. You can also leverage the [strict](#strict-proxy-providers) mode to find out which Proxy Providers are not yet resolved.
Copy file name to clipboardExpand all lines: docs/docs/04_api/02_module-options.md
+3Lines changed: 3 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,9 @@ The `ClsModuleProxyFactoryProviderOptions` interface further accepts:
58
58
-**_`type?:`_**`'function' | 'object'`
59
59
Whether the Proxy Provider should be a function or an object. Defaults to `'object'`. See [Caveats](../03_features-and-use-cases/06_proxy-providers.md#caveats) for more information.
60
60
61
+
-**_`strict?:`_**`boolean`
62
+
Whether to register this Proxy Provider in [strict mode](../03_features-and-use-cases/06_proxy-providers.md#strict-proxy-providers). Defaults to `false`.
63
+
61
64
## Middleware & Enhancer options
62
65
63
66
All of the **`Cls{Middleware,Guard,Interceptor}Options`** take the following parameters (either in `ClsModuleOptions` or directly when instantiating them manually):
0 commit comments