-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: add missing files to fix dead links
- Loading branch information
1 parent
17cf87d
commit 5068610
Showing
3 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Custom providers | ||
|
||
There are a lot of scenarios where you might want to bind something directly to the IoC container. | ||
For example, any constant values, configuration objects created based on the current environment, | ||
external libraries, or pre-calculated values that depend on few other defined providers. | ||
|
||
Moreover, you are able to override default implementations, e.g. use different classes or make use of various test doubles (for testing purposes) when needed. | ||
|
||
One essential thing that you should always keep in mind is that Ts.ED uses @@TokenProvider@@ to identify a depencency. | ||
|
||
Usually, the auto-generated tokens are equal to classes. If you want to create a custom provider, you'd need to choose a token. | ||
Mostly, the custom tokens are represented by either plain strings or symbols. | ||
|
||
Let's go through the available options. | ||
|
||
## Use Value | ||
|
||
The `useValue` syntax is useful when it comes to either define a constant value, put external library into DI container, | ||
or replace a real implementation with the mock object. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-value-declaration.ts | ||
|
||
In order to inject custom provider, we use the @@Inject@@ decorator. This decorator takes a single argument - the token. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-value-usage.ts | ||
|
||
## Use Factory | ||
|
||
The `useFactory` is a way of creating providers dynamically. | ||
The actual provider will be equal to a returned value of the factory function. | ||
The factory function can either depend on several different providers or stay completely independent. | ||
It means that factory may accept arguments, that DI will resolve and pass during the instantiation process. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-factory-declaration.ts | ||
|
||
In order to inject a custom provider, we use the @@Inject@@ decorator. This decorator takes a single argument - the token. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-value-usage.ts | ||
|
||
::: tip | ||
Since v6.110.0, factory and custom provider can register his own [hooks](/docs/hooks.md)! | ||
::: | ||
|
||
## Use Async Factory | ||
|
||
The `useAsyncFactory` is a way of creating asynchronous providers dynamically. | ||
The actual provider will be equal to a returned value of the factory function. | ||
The factory function can either depend on several different providers or stay completely independent. | ||
It means that factory may accept arguments, that DI will resolve and pass during the instantiation process. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-async-factory-declaration.ts | ||
|
||
In order to inject a custom provider, we use the @@Inject@@ decorator. This decorator takes a single argument - the token. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-value-usage.ts | ||
|
||
::: warning | ||
Async factory will always be considered as `SINGLETON`. It is not possible to use other scopes like `REQUEST` and `INSTANCE` because asynchronous providers are resolved on server loading. | ||
::: | ||
|
||
## Use Class | ||
|
||
The `useClass` syntax is similar to register provider via decorator. But it allows you to use different classes per chosen factors. | ||
For example you can change the class depending on the environment profile `production` or `development`. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-class-declaration.ts | ||
|
||
::: tip | ||
registerProvider can be used to add a provider or override an existing provider (like @@OverrideProvider@@ decorator). | ||
::: | ||
|
||
In this case, even if any class depends on ConfigService, Ts.ED will inject an instance of the provided class (`ConfigService` or `DevConfigService`) instead. | ||
|
||
<<< @/docs/snippets/providers/custom-provider-use-class-usage.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Injection scopes | ||
|
||
The scope of a [Provider](/docs/providers.md) defines the lifecycle and visibility of that bean in the context in which it is used. | ||
|
||
Ts.ED DI defines 3 types of @@Scope@@: `singleton`, `request` and `instance` which can be used on `Provider`, `Service`, `Middleware` and `Controller`. | ||
|
||
## Singleton scope | ||
|
||
Singleton scope is the default behavior of all providers. That means all providers are created during server initialization. | ||
|
||
<<< @/docs/snippets/providers/scope-singleton.ts | ||
|
||
::: tip Note | ||
In this example all requests on `/random` endpoint return the same random value. | ||
::: | ||
|
||
## Request scope | ||
|
||
Request scope will create a new instance of provider for each incoming request. A new container will be created | ||
and attached to the request. It will contain all providers annotated by `@Scope(ProviderScope.REQUEST)`. | ||
|
||
<<< @/docs/snippets/providers/scope-request.ts | ||
|
||
Each request on `/random` will return a different random value. | ||
|
||
### Chain with Service | ||
|
||
It is also possible to use `@Scope(ProviderScope.REQUEST)` on a service if your service is injected on a controller | ||
which is annotated by `@Scope(ProviderScope.REQUEST)` too. | ||
|
||
Here is a working example: | ||
|
||
<<< @/docs/snippets/providers/scope-chain.ts | ||
|
||
And here is an unworking example: | ||
|
||
<<< @/docs/snippets/providers/scope-chain-fail.ts | ||
|
||
::: warning | ||
The `SINGLETON` annotation avoids the `@Scope(ProviderScope.REQUEST)` annotation put on MyService. | ||
::: | ||
|
||
::: warning | ||
The `@Scope(ProviderScope.REQUEST)` annotation has no effect on Global middlewares. | ||
::: | ||
|
||
### Performance | ||
|
||
Using request-scoped providers will obviously affect application performance. | ||
Even though Ts.ED is trying to cache as much metadata as possible, it will still have to create an instance of your class on each request. | ||
Hence, it will slow down your average response time and overall benchmarking result. | ||
If your provider doesn't necessarily need to be request-scoped, you should rather stick with the singleton scope. | ||
|
||
## Instance scope | ||
|
||
Instance scope used on a provider tells the injector to create a new instance each time the provider is injected to another one. | ||
|
||
<<< @/docs/snippets/providers/scope-instance.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
--- | ||
meta: | ||
- name: description | ||
content: Documentation over Providers / DI provided by Ts.ED framework. Use providers to build your backend services. | ||
- name: keywords | ||
content: providers di ioc ts.ed express typescript node.js javascript decorators jsonschema class models | ||
--- | ||
|
||
# Lazy-loading provider | ||
|
||
<Badge text="6.81.0+"/> | ||
|
||
By default, modules are eagerly loaded, which means that as soon as the application loads, so do all the modules, | ||
whether or not they are immediately necessary. While this is fine for most applications, it may become a bottleneck for | ||
apps running in the **serverless environment**, where the startup latency `("cold start")` is crucial. | ||
|
||
Lazy loading can help decrease bootstrap time by loading only modules required by the specific serverless function | ||
invocation. In addition, you could also load other modules asynchronously once the serverless function is "warm" to | ||
speed-up the bootstrap time for subsequent calls even further (deferred modules registration). | ||
|
||
## Getting started | ||
|
||
To load a provider on-demand, Ts.ED provide decorators @@LazyInject@@ and @@OptionalLazyInject@@. Here is an example | ||
with a @@PlatformExceptions@@: | ||
|
||
```typescript | ||
import { Injectable, LazyInject } from "@tsed/di"; | ||
import type { PlatformExceptions } from "@tsed/platform-exceptions"; | ||
|
||
@Injectable() | ||
class MyInjectable { | ||
@LazyInject("PlatformException", () => import("@tsed/platform-exceptions")) | ||
private platformExceptions: Promise<PlatformExceptions>; | ||
|
||
async use() { | ||
try { | ||
/// do something | ||
} catch (er) { | ||
const exceptions = await this.platformExceptions; | ||
platformExceptions.catch(er, {}); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
The LazyInject decorator will load the `node_module` and invoke automatically the `PlatformException` exported class, | ||
only when the decorated property will be used by your code. | ||
|
||
::: tip | ||
|
||
Lazy-loaded provider is `cached` on the first call. | ||
That means, each consecutive call will be very fast and will return a cached instance, instead of loading the provider again. | ||
|
||
::: | ||
|
||
Create you own lazy injectable doesn't require special things, just declare a module or an injectable service is enough: | ||
|
||
```typescript | ||
import { Module } from "@tsed/di"; | ||
|
||
@Module({ | ||
// works also with @Injectable | ||
imports: [], // Use the imports field if you have services to build | ||
}) | ||
export class MyModule { | ||
$onInit() { | ||
// The hook will be called once the module is loaded | ||
} | ||
} | ||
``` | ||
|
||
Then use it: | ||
|
||
```typescript | ||
import { Injectable, LazyInject } from "@tsed/di"; | ||
import type { MyModule } from "../MyModule.ts"; | ||
|
||
@Injectable() | ||
class MyInjectable { | ||
@LazyInject("MyModule", () => import("../MyModule.ts")) | ||
private myModule: Promise<MyModule>; | ||
|
||
async use() { | ||
const myModule = await this.myModule; | ||
|
||
myModule.doSomething(); | ||
} | ||
} | ||
``` | ||
|
||
::: warning | ||
|
||
If you use Webpack, make sure to update your `tsconfig.json` file - setting `compilerOptions.module` to `"esnext"` and adding `compilerOptions.moduleResolution` property with `"node"` as a value: | ||
|
||
```json | ||
{ | ||
"compilerOptions": { | ||
"module": "esnext", | ||
"moduleResolution": "node" | ||
} | ||
} | ||
``` | ||
|
||
::: | ||
|
||
## Lazy loading programmatically | ||
|
||
Ts.ED provide also a way to lazy load a provider programmatically. You just need to inject the @@InjectorService@@ in service: | ||
|
||
```typescript | ||
import { Injectable, Inject, InjectorService } from "@tsed/di"; | ||
|
||
@Injectable() | ||
class MyService { | ||
@Inject() | ||
protected injector: InjectorService; | ||
|
||
async load() { | ||
const { MyModule } = await import("../lazy/my-module.ts"); | ||
const myModule = await this.injector.lazyInvoke(MyModule); | ||
|
||
myModule.doSomething(); | ||
} | ||
} | ||
``` | ||
|
||
## Limitation | ||
|
||
Some providers cannot be lazy loaded: | ||
|
||
- Controllers, | ||
- Middlewares, | ||
- All providers that need to run a specific hook (excepted `$onInit` hook). |