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 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 {Injectable} 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 as the same effect. @Injectable accept options, @Service 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 CatsController {
      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 {ServerLoader, ServerSettings} from "@tsed/common";
    import {CalendarsCtrl} from "./controllers/CalendarsCtrl";
    import {CalendarsService} from "./services/CalendarsService";
    
    @ServerSettings({
      mount: {
        "/rest": [CalendarsCtrl]
      },
      componentsScan: [
        CalendarsService
      ]
    })
    export class Server extends ServerLoader {
    }
    
    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 emit type metadata on the constructor which will be exploited by the to resolve dependencies automatically.

    constructor(private calendarsService: CalendarsService) {}
    
    1

    Scopes

    All providers has a lifetime strictly dependent on the application lifecycle. Once the server is created, all providers have to be instantiated. Similarly, when the application shutdown, 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 configuration set in or with can be retrieved with and decorators. Theses decorators can be used with:

    and accept an expression as parameters 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

    return 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 resolve 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 !