diff --git a/docs/docs/providers.md b/docs/docs/providers.md
index e69de29..1499094 100644
--- a/docs/docs/providers.md
+++ b/docs/docs/providers.md
@@ -0,0 +1,230 @@
+---
+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
+---
+
+# Providers
+
+Basically, almost everything may be considered as a provider – service, factory, interceptors, and so on. All of them
+can inject dependencies, meaning, they can create various relationships with each other. But in fact, a provider is
+nothing else than just a simple class annotated with an `@Injectable()` decorator.
+
+
+
+In controllers chapter, we've seen how to build a Controller, handle a request and create a response. Controllers shall
+handle HTTP requests and delegate complex tasks to the **providers**.
+
+The providers are plain javascript class and use one of these decorators on top of them. Here is the list:
+
+
+
+## Services
+
+Let's start by creating a simple CalendarService provider.
+
+<<< @/docs/snippets/providers/getting-started-service.ts
+
+::: tip Note
+
+@@Service@@ and @@Injectable@@ have the same effect. @@Injectable@@ accepts options, @@Service@@ does not.
+A Service is always configured as `singleton`.
+
+Example with @@Injectable@:
+
+<<< @/docs/snippets/providers/getting-started-injectable.ts
+
+:::
+
+Now we have the service class already done, let's use it inside the `CalendarsController`:
+
+<<< @/docs/snippets/providers/getting-started-controller.ts
+
+Finally, we can load the injector and use it:
+
+<<< @/docs/snippets/providers/getting-started-serverloader.ts
+
+::: tip NOTE
+
+You'll notice that we only import the CalendarsController and not the CalendarsService as that would be the case
+with other DIs (Angular / inversify). Ts.ED will discover automatically services/providers as soon as it is imported
+into your application via an import ES6.
+
+In most case, if a service is used by a controller or another service which is used by a controller, it's not necessary
+to import it explicitly!
+:::
+
+## Dependency injection
+
+Ts.ED is built around the **dependency injection** pattern. TypeScript emits type metadata on the constructor which will
+be exploited by the @@InjectorService@@ to resolve dependencies automatically.
+
+```typescript
+import { Injectable } from "@tsed/di";
+
+@Injectable()
+class MyInjectable {
+ constructor(private calendarsService: CalendarsService) {}
+}
+```
+
+It's also possible to inject a service on a property by using @@Inject@@ decorator:
+
+```typescript
+import { Injectable, Inject } from "@tsed/di";
+
+@Injectable()
+class MyInjectable {
+ @Inject()
+ private calendarsService: CalendarService;
+
+ $onInit() {
+ console.log(this.calendarsService);
+ }
+}
+```
+
+In this case, the service won't be usable in the constructor. If you have to do something with the injected service,
+you can use the `$onInit` hook.
+
+## Scopes
+
+All providers have a lifetime strictly dependent on the application lifecycle. Once the server is created, all providers
+have to be instantiated. Similarly, when the application shuts down, all providers will be destroyed. However, there are
+ways to make your provider lifetime **request-scoped** as well. You can read more about these
+techniques [here](/docs/injection-scopes.md).
+
+## Binding configuration
+
+All configurations set with @@Module@@ or @@Configuration@@ can be retrieved with @@Constant@@ and @@Value@@ decorators.
+These decorators can be used with:
+
+- [Service](/docs/services.md),
+- [Controller](/docs/controllers.md),
+- [Middleware](/docs/middlewares.md),
+- [Pipes](/docs/pipes.md).
+
+@@Constant@@ and @@Value@@ accept an expression as parameter to inspect the configuration object and return the value.
+
+<<< @/docs/snippets/providers/binding-configuration.ts
+
+::: warning
+
+@@Constant@@ returns an Object.freeze() value.
+:::
+
+::: tip NOTE
+
+The values for the decorated properties aren't available on constructor. Use \$onInit() hook to use the
+value.
+:::
+
+## Custom providers
+
+The Ts.ED IoC resolves relationships providers for you, but sometimes, you want to tell to the DI how you want to
+instantiate a specific service or inject different kind of providers based on values, on asynchronous or synchronous
+factory or on external library. Look [here](/docs/custom-providers.md) to find more examples.
+
+## Configurable provider
+
+Sometimes you need to inject a provider with a specific configuration to another one.
+
+This is possible with the combination of @@Opts@@ and @@UseOpts@@ decorators.
+
+<<< @/docs/snippets/providers/configurable-provider.ts
+
+::: warning
+
+Using @@Opts@@ decorator on a constructor parameter changes the scope of the provider
+to `ProviderScope.INSTANCE`.
+:::
+
+## Inject many provider
+
+This feature simplifies dependency management when working with multiple implementations of the same interface using type code.
+
+If users use the same token when registering providers, the IoC container should exchange a token for a list of instances. Let's consider the following real example:
+
+```typescript
+interface Bar {
+ type: string;
+}
+
+const Bar: unique symbol = Symbol("Bar");
+
+@Injectable({ type: Bar })
+class Foo implements Bar {
+ private readonly type = "foo";
+}
+
+@Injectable({ type: Bar })
+class Baz implements Bar {
+ private readonly type = "baz";
+}
+```
+
+Now as a user, I would like to create a [registry](https://www.martinfowler.com/eaaCatalog/registry.html) and retrieve an appropriate instance by type:
+
+```typescript
+@Controller("/some")
+export class SomeController {
+ constructor(@Inject(Bar) private readonly bars: Bar[]) {}
+
+ @Post()
+ async create(@Body("type") type: "baz" | "foo") {
+ const bar: Bar | undefined = this.bars.find((x) => x.type === type);
+ }
+}
+```
+
+or in the following way as well:
+
+```typescript
+@Controller("/some")
+export class SomeController {
+ constructor(private readonly injector: InjectorService) {}
+
+ @Post()
+ async create(@Body("type") type: "baz" | "foo") {
+ const bars: Bar[] = this.injector.getAll(Bar);
+ const bar: Bar | undefined = bars.find((x) => x.type === type);
+
+ // your code
+ }
+}
+```
+
+## Override an injection token
+
+By default, the `@Injectable()` decorator registers a class provider using an injection token obtained from the metadata generated by TypeScript.
+That means that you have to use a concrete class as a token to resolve a provider.
+
+To override an injection token, that is needed to resolve an instance, use the `@Injectable` decorator like this:
+
+<<< @/docs/snippets/providers/override-injection-token.ts
+
+An injection token may be either a string, a symbol, a class constructor.
+
+> Just don't forget to import your provider in your project !
+
+## Lazy load provider
+
+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).
+
+You can read more about these techniques [here](/docs/providers-lazy-loading.md).
+
+## Override provider
+
+Any provider (Provider, Service, Controller, Middleware, etc...) already registered by Ts.ED or third-party can be
+overridden by your own class.
+
+<<< @/docs/snippets/providers/override-provider.ts
+
+> Just don't forget to import your provider in your project !
diff --git a/docs/docs/snippets/providers/binding-configuration.ts b/docs/docs/snippets/providers/binding-configuration.ts
new file mode 100644
index 0000000..317afd5
--- /dev/null
+++ b/docs/docs/snippets/providers/binding-configuration.ts
@@ -0,0 +1,14 @@
+import {Constant, Value} from "@tsed/di";
+import {Env} from "@tsed/core";
+
+export class MyClass {
+ @Constant("env")
+ env: Env;
+
+ @Value("swagger.path")
+ swaggerPath: string;
+
+ $onInit() {
+ console.log(this.env);
+ }
+}
diff --git a/docs/docs/snippets/providers/configurable-provider.ts b/docs/docs/snippets/providers/configurable-provider.ts
new file mode 100644
index 0000000..0f58898
--- /dev/null
+++ b/docs/docs/snippets/providers/configurable-provider.ts
@@ -0,0 +1,26 @@
+import {Injectable, Opts, UseOpts} from "@tsed/di";
+
+@Injectable()
+class MyConfigurableService {
+ source: string;
+
+ constructor(@Opts options: any = {}) {
+ console.log("Hello ", options.source); // log: Hello Service1 then Hello Service2
+
+ this.source = options.source;
+ }
+}
+
+@Injectable()
+class MyService1 {
+ constructor(@UseOpts({source: "Service1"}) service: MyConfigurableService) {
+ console.log(service.source); // log: Service1
+ }
+}
+
+@Injectable()
+class MyService2 {
+ constructor(@UseOpts({source: "Service2"}) service: MyConfigurableService) {
+ console.log(service.source); // log: Service2
+ }
+}
diff --git a/docs/docs/snippets/providers/custom-provider-use-async-factory-declaration.ts b/docs/docs/snippets/providers/custom-provider-use-async-factory-declaration.ts
new file mode 100644
index 0000000..9015ec3
--- /dev/null
+++ b/docs/docs/snippets/providers/custom-provider-use-async-factory-declaration.ts
@@ -0,0 +1,17 @@
+import {Configuration, registerProvider} from "@tsed/di";
+import {DatabaseConnection} from "connection-lib";
+
+export const CONNECTION = Symbol.for("CONNECTION");
+
+registerProvider({
+ provide: CONNECTION,
+ deps: [Configuration],
+ async useAsyncFactory(settings: Configuration) {
+ const options = settings.get("myOptions");
+ const connection = new DatabaseConnection(options);
+
+ await connection.connect();
+
+ return connection;
+ }
+});
diff --git a/docs/docs/snippets/providers/custom-provider-use-class-declaration.ts b/docs/docs/snippets/providers/custom-provider-use-class-declaration.ts
new file mode 100644
index 0000000..0110298
--- /dev/null
+++ b/docs/docs/snippets/providers/custom-provider-use-class-declaration.ts
@@ -0,0 +1,11 @@
+import {EnvTypes} from "@tsed/core";
+import {registerProvider} from "@tsed/di";
+
+export class ConfigService {}
+
+export class DevConfigService {}
+
+registerProvider({
+ provide: ConfigService,
+ useClass: process.env.NODE_ENV === EnvTypes.PROD ? ConfigService : DevConfigService
+});
diff --git a/docs/docs/snippets/providers/custom-provider-use-class-usage.ts b/docs/docs/snippets/providers/custom-provider-use-class-usage.ts
new file mode 100644
index 0000000..d97560a
--- /dev/null
+++ b/docs/docs/snippets/providers/custom-provider-use-class-usage.ts
@@ -0,0 +1,9 @@
+import {Injectable} from "@tsed/di";
+import {ConfigService} from "./ConfigService";
+
+@Injectable()
+export class MyService {
+ constructor(configService: ConfigService) {
+ console.log(process.env.NODE_ENV, configService); // DevConfigService or ConfigService
+ }
+}
diff --git a/docs/docs/snippets/providers/custom-provider-use-factory-declaration.ts b/docs/docs/snippets/providers/custom-provider-use-factory-declaration.ts
new file mode 100644
index 0000000..ea14604
--- /dev/null
+++ b/docs/docs/snippets/providers/custom-provider-use-factory-declaration.ts
@@ -0,0 +1,19 @@
+import {Configuration, registerProvider} from "@tsed/di";
+import {DatabaseConnection} from "connection-lib";
+
+export const CONNECTION = Symbol.for("CONNECTION");
+
+registerProvider({
+ provide: CONNECTION,
+ deps: [Configuration],
+ useFactory(configuration: Configuration) {
+ const options = configuration.get("myOptions");
+
+ return new DatabaseConnection(options);
+ },
+ hooks: {
+ $onDestroy(connection) {
+ return connection.close();
+ }
+ }
+});
diff --git a/docs/docs/snippets/providers/custom-provider-use-value-declaration.ts b/docs/docs/snippets/providers/custom-provider-use-value-declaration.ts
new file mode 100644
index 0000000..8c7b91d
--- /dev/null
+++ b/docs/docs/snippets/providers/custom-provider-use-value-declaration.ts
@@ -0,0 +1,14 @@
+import {registerProvider} from "@tsed/di";
+import {connection} from "connection-lib";
+
+export const CONNECTION = Symbol.for("CONNECTION");
+
+registerProvider({
+ provide: CONNECTION,
+ useValue: connection,
+ hooks: {
+ $onDestroy(connection: any) {
+ return connection.close();
+ }
+ }
+});
diff --git a/docs/docs/snippets/providers/custom-provider-use-value-usage.ts b/docs/docs/snippets/providers/custom-provider-use-value-usage.ts
new file mode 100644
index 0000000..befc2e2
--- /dev/null
+++ b/docs/docs/snippets/providers/custom-provider-use-value-usage.ts
@@ -0,0 +1,7 @@
+import {Inject, Injectable} from "@tsed/di";
+import {CONNECTION} from "./connection";
+
+@Injectable()
+export class MyService {
+ constructor(@Inject(CONNECTION) connection: any) {}
+}
diff --git a/docs/docs/snippets/providers/getting-started-controller.ts b/docs/docs/snippets/providers/getting-started-controller.ts
new file mode 100644
index 0000000..dc2207d
--- /dev/null
+++ b/docs/docs/snippets/providers/getting-started-controller.ts
@@ -0,0 +1,20 @@
+import {BodyParams} from "@tsed/platform-params";
+import {Get, Post} from "@tsed/schema";
+import {Controller} from "@tsed/di";
+import {Calendar} from "../models/Calendar";
+import {CalendarsService} from "../services/CalendarsService";
+
+@Controller("/calendars")
+export class CalendarsController {
+ constructor(private readonly calendarsService: CalendarsService) {}
+
+ @Post()
+ async create(@BodyParams() calendar: Calendar) {
+ this.calendarsService.create(calendar);
+ }
+
+ @Get()
+ async findAll(): Promise {
+ return this.calendarsService.findAll();
+ }
+}
diff --git a/docs/docs/snippets/providers/getting-started-injectable.ts b/docs/docs/snippets/providers/getting-started-injectable.ts
new file mode 100644
index 0000000..de64c72
--- /dev/null
+++ b/docs/docs/snippets/providers/getting-started-injectable.ts
@@ -0,0 +1,18 @@
+import {Injectable, ProviderScope, ProviderType} from "@tsed/di";
+import {Calendar} from "../models/Calendar";
+
+@Injectable({
+ type: ProviderType.SERVICE,
+ scope: ProviderScope.SINGLETON
+})
+export class CalendarsService {
+ private readonly calendars: Calendar[] = [];
+
+ create(calendar: Calendar) {
+ this.calendars.push(calendar);
+ }
+
+ findAll(): Calendar[] {
+ return this.calendars;
+ }
+}
diff --git a/docs/docs/snippets/providers/getting-started-serverloader.ts b/docs/docs/snippets/providers/getting-started-serverloader.ts
new file mode 100644
index 0000000..a70e7eb
--- /dev/null
+++ b/docs/docs/snippets/providers/getting-started-serverloader.ts
@@ -0,0 +1,9 @@
+import {Configuration} from "@tsed/di";
+import {CalendarsController} from "./controllers/CalendarsController";
+
+@Configuration({
+ mount: {
+ "/rest": [CalendarsController]
+ }
+})
+export class Server {}
diff --git a/docs/docs/snippets/providers/getting-started-service.ts b/docs/docs/snippets/providers/getting-started-service.ts
new file mode 100644
index 0000000..54a76db
--- /dev/null
+++ b/docs/docs/snippets/providers/getting-started-service.ts
@@ -0,0 +1,15 @@
+import {Injectable} from "@tsed/di";
+import {Calendar} from "../models/Calendar";
+
+@Injectable()
+export class CalendarsService {
+ private readonly calendars: Calendar[] = [];
+
+ create(calendar: Calendar) {
+ this.calendars.push(calendar);
+ }
+
+ findAll(): Calendar[] {
+ return this.calendars;
+ }
+}
diff --git a/docs/docs/snippets/providers/override-injection-token.ts b/docs/docs/snippets/providers/override-injection-token.ts
new file mode 100644
index 0000000..6faa514
--- /dev/null
+++ b/docs/docs/snippets/providers/override-injection-token.ts
@@ -0,0 +1,21 @@
+import {Inject, Injectable} from "@tsed/di";
+
+export interface RetryPolicy {
+ retry unknown>(task: T): Promise>;
+}
+
+export const RetryPolicy: unique symbol = Symbol("RetryPolicy");
+
+@Injectable({provide: RetryPolicy})
+export class TokenBucket implements RetryPolicy {
+ public retry unknown>(task: T): Promise> {
+ // ...
+ }
+}
+
+@Injectable()
+export class MyService {
+ constructor(@Inject(RetryPolicy) private readonly retryPolicy: RetryPolicy) {
+ // an instance of `TokenBucket`
+ }
+}
diff --git a/docs/docs/snippets/providers/override-provider.ts b/docs/docs/snippets/providers/override-provider.ts
new file mode 100644
index 0000000..cf77c14
--- /dev/null
+++ b/docs/docs/snippets/providers/override-provider.ts
@@ -0,0 +1,10 @@
+import {OriginalService} from "@tsed/common";
+import {OverrideProvider} from "@tsed/di";
+
+@OverrideProvider(OriginalService)
+export class CustomMiddleware extends OriginalService {
+ public method() {
+ // Do something
+ return super.method();
+ }
+}
diff --git a/docs/docs/snippets/providers/scope-chain-fail.ts b/docs/docs/snippets/providers/scope-chain-fail.ts
new file mode 100644
index 0000000..e4a2470
--- /dev/null
+++ b/docs/docs/snippets/providers/scope-chain-fail.ts
@@ -0,0 +1,19 @@
+import {Get} from "@tsed/schema";
+import {Controller, Injectable, ProviderScope, Scope} from "@tsed/di";
+
+@Injectable()
+@Scope(ProviderScope.REQUEST)
+export class MyService {
+ public rand = Math.random() * 100;
+}
+
+@Controller("/")
+@Scope(ProviderScope.SINGLETON) // SINGLETON avoid all Scope("request") annotation
+export class MyController {
+ constructor(private myService: MyService) {}
+
+ @Get("/random")
+ async getValue() {
+ return this.myService.rand;
+ }
+}
diff --git a/docs/docs/snippets/providers/scope-chain.ts b/docs/docs/snippets/providers/scope-chain.ts
new file mode 100644
index 0000000..ae926ac
--- /dev/null
+++ b/docs/docs/snippets/providers/scope-chain.ts
@@ -0,0 +1,19 @@
+import {Get} from "@tsed/schema";
+import {Controller, Injectable, ProviderScope, Scope} from "@tsed/di";
+
+@Injectable()
+@Scope(ProviderScope.REQUEST)
+export class MyService {
+ public rand = Math.random() * 100;
+}
+
+@Controller("/")
+@Scope(ProviderScope.REQUEST)
+export class MyController {
+ constructor(private myService: MyService) {}
+
+ @Get("/random")
+ async getValue() {
+ return this.myService.rand;
+ }
+}
diff --git a/docs/docs/snippets/providers/scope-instance.ts b/docs/docs/snippets/providers/scope-instance.ts
new file mode 100644
index 0000000..5d54ede
--- /dev/null
+++ b/docs/docs/snippets/providers/scope-instance.ts
@@ -0,0 +1,23 @@
+import {Get} from "@tsed/schema";
+import {Controller, Injectable, ProviderScope, Scope} from "@tsed/di";
+
+@Injectable()
+@Scope(ProviderScope.INSTANCE)
+export class MyInstanceService {
+ private rand = Math.random() * 100;
+
+ @Get("/random")
+ async getValue() {
+ return this.rand;
+ }
+}
+
+@Controller("/")
+@Scope(ProviderScope.SINGLETON)
+export class MyController {
+ constructor(instance1: MyInstanceService, instance2: MyInstanceService) {
+ console.log("IsSame", instance1 === instance2);
+ console.log("instance1", instance1.getValue());
+ console.log("instance2", instance2.getValue());
+ }
+}
diff --git a/docs/docs/snippets/providers/scope-request.ts b/docs/docs/snippets/providers/scope-request.ts
new file mode 100644
index 0000000..acf44e9
--- /dev/null
+++ b/docs/docs/snippets/providers/scope-request.ts
@@ -0,0 +1,13 @@
+import {Get} from "@tsed/schema";
+import {Controller, ProviderScope, Scope} from "@tsed/di";
+
+@Controller("/")
+@Scope(ProviderScope.REQUEST)
+export class MyController {
+ private rand = Math.random() * 100;
+
+ @Get("/random")
+ async getValue() {
+ return this.rand;
+ }
+}
diff --git a/docs/docs/snippets/providers/scope-singleton.ts b/docs/docs/snippets/providers/scope-singleton.ts
new file mode 100644
index 0000000..5a2ae01
--- /dev/null
+++ b/docs/docs/snippets/providers/scope-singleton.ts
@@ -0,0 +1,13 @@
+import {Get} from "@tsed/schema";
+import {Controller, ProviderScope, Scope} from "@tsed/di";
+
+@Controller("/")
+@Scope(ProviderScope.SINGLETON) // OPTIONAL, leaving this annotation a the same behavior
+export class MyController {
+ private rand = Math.random() * 100;
+
+ @Get("/random")
+ async getValue() {
+ return this.rand;
+ }
+}