# 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

Service and Injectable have the same effect. Injectable accepts options, Service 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.SERVICE,
  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";

@Configuration({
  mount: {
    "/rest": [CalendarsCtrl]
  }
})
export class Server {
}
1
2
3
4
5
6
7
8
9
10

NOTE

You'll notice that we only import the CalendarsCtrl 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.

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 Unable to find something: symbolName === "ServerSettings", Module or Configuration can be retrieved with Constant and Value decorators. Theses decorators can be used with:

Constant and Value 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

Constant 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

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.

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 Opts decorator on a constructor parameter changes the scope of the provider to ProviderScope.INSTANCE.

Last Updated: 9/17/2021, 8:57:54 AM

Other topics