# 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 the list:

# Services

Let's start by creating a simple CalendarService provider.

import {Service} from "@tsed/common";
import {Calendar} from "../models/Calendar";

@Service()
export class CalendarsService {
  private readonly calendars: Calendar[] = [];

  create(calendar: Calendar) {
    this.calendars.push(calendar);
  }

  findAll(): Calendar[] {
    return this.calendars;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Note

and have the same effect. accepts options, does not. A Service is always configured as singleton.

Example with @@Injectable@:

import {Injectable, ProviderScope, ProviderType} from "@tsed/common";
import {Calendar} from "../models/Calendar";

@Injectable({
  type: ProviderType.CONTROLLER,
  scope: ProviderScope.SINGLETON
})
export class CalendarsService {
  private readonly calendars: Calendar[] = [];

  create(calendar: Calendar) {
    this.calendars.push(calendar);
  }

  findAll(): Calendar[] {
    return this.calendars;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Now we have the service class already done, let's use it inside the CalendarCtrl:

import {BodyParams, Controller, Get, Post} from "@tsed/common";
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<Calendar[]> {
    return this.calendarsService.findAll();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Finally, we can load the injector and use it:

import {Configuration} from "@tsed/common";
import {CalendarsCtrl} from "./controllers/CalendarsCtrl";
import {CalendarsService} from "./services/CalendarsService";

@Configuration({
  mount: {
    "/rest": [CalendarsCtrl]
  },
  componentsScan: [
    CalendarsService
  ]
})
export class Server {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Dependency injection

Ts.ED is built around the dependency injection pattern. TypeScript emits type metadata on the constructor which will be exploited by the to resolve dependencies automatically.

constructor(private calendarsService: CalendarsService) {}
1

# 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.

# Binding configuration

All configurations set with , or can be retrieved with and decorators. Theses decorators can be used with:

and accept an expression as parameter to inspect the configuration object and return the value.

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);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

WARNING

returns an Object.freeze() value.

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 to find more examples.

# Override provider

Any provider (Provider, Service, Controller, Middleware, etc...) already registered by Ts.ED or third-party can be overridden by your own class.

import {OriginalService, OverrideProvider} from "@tsed/common";

@OverrideProvider(OriginalService)
export class CustomMiddleware extends OriginalService {
  public method() {
    // Do something
    return super.method();
  }
}
1
2
3
4
5
6
7
8
9

Just don't forgot to import your provider in your project !

# Configurable provider v5.58.0+

Sometimes you need to inject a provider with a specific configuration to another one.

This is possible with the combination of and decorators.

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
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

WARNING

Using decorator on a constructor parameter changes the scope of the provider to ProviderScope.INSTANCE.