Custom providers

There are a lot of scenarios when 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 depends 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 use 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.

import {registerProvider} from "@tsed/di";
import {connection} from "connection-lib";

export const CONNECTION = Symbol.for("CONNECTION");

registerProvider({
  provide: CONNECTION,
  useValue: connection
});
1
2
3
4
5
6
7
8
9

In order to inject custom provider, we use the decorator. This decorator takes a single argument - the token.

import {Inject, Injectable} from "@tsed/common";
import {CONNECTION} from "./connection";

@Injectable()
export class MyService {
  constructor(@Inject(CONNECTION) connection: any) {

  }
}
1
2
3
4
5
6
7
8
9

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.

import {ServerSettingsService} from "@tsed/common";
import {registerProvider} from "@tsed/di";
import {DatabaseConnection} from "connection-lib";

export const CONNECTION = Symbol.for("CONNECTION");

registerProvider({
  provide: CONNECTION,
  deps: [ServerSettingsService],
  useFactory(settings: ServerSettingsService) {
    const options = settings.get("myOptions");

    return new DatabaseConnection(options);
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

In order to inject custom provider, we use the decorator. This decorator takes a single argument - the token.

import {Inject, Injectable} from "@tsed/common";
import {CONNECTION} from "./connection";

@Injectable()
export class MyService {
  constructor(@Inject(CONNECTION) connection: any) {

  }
}
1
2
3
4
5
6
7
8
9

Built-in factory

Some factories are built-in Ts.ED. These factories are :

  • : This is an instance of Express.Application](http://expressjs.com/fr/4x/api.html#app).
  • : This is an instance of Http.Server from http module.
  • : This is an instance of Https.Server from https module.

Use Class

The useClass syntax is similar to register provider via decorator. But it allows you using different class per chosen factors. For example you can change class depending on the environment profile production or development.

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

TIP

registerProvider can use for to add a provider or override an existing provider (like 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.

import {Injectable} from "@tsed/common";
import {ConfigService} from "./ConfigService";

@Injectable()
export class MyService {
  constructor(configService: ConfigService) {
    console.log(process.env.NODE_ENV, configService); // DevConfigService or ConfigService
  }
}
1
2
3
4
5
6
7
8
9