Skip to content

Providers

Aleksandr Melnik edited this page Oct 18, 2024 · 9 revisions

Providers in Nexus IoC

Providers in Nexus IoC are fundamental components that define how the IoC container should resolve dependencies. They allow for flexible and modular configuration of how instances are created and managed. There are several types of providers: class providers, value providers, factory providers, and existing providers.

Class Provider

A class provider tells the IoC container to instantiate a class when a dependency is requested. This is the most common type of provider.

import { NsModule, Injectable } from '@nexus-ioc/core';

@Injectable()
class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

@NsModule({
  providers: [AppService],
})
export class AppModule {}

Class Provider with useClass

A class provider can also use the useClass property to specify a different implementation class.

Purpose

To provide an alternate class implementation for an interface or abstract class.

import { NsModule, Injectable } from '@nexus-ioc/core';

interface GreetingService {
  getGreeting(): string;
}

@Injectable()
class EnglishGreetingService implements GreetingService {
  getGreeting(): string {
    return 'Hello!';
  }
}

@Injectable()
class SpanishGreetingService implements GreetingService {
  getGreeting(): string {
    return '¡Hola!';
  }
}

@NsModule({
  providers: [
    { provide: 'GreetingService', useClass: EnglishGreetingService },  // Use EnglishGreetingService as the implementation
  ],
})
export class AppModule {}

Value Provider

A value provider tells the IoC container to use a specific value for the dependency. This is useful for configuration values or constants.

Purpose

  • To provide a constant or configuration value.
  • To share common values across multiple components.
import { NsModule, Injectable, Inject } from '@nexus-ioc/core';

const CONFIG = { apiUrl: 'https://api.example.com' };

@Injectable()
class AppService {
  constructor(
    @Inject('CONFIG') private readonly config: typeof CONFIG,
  ) {}

  getApiUrl(): string {
    return this.config.apiUrl;
  }
}

@NsModule({
  providers: [{ provide: 'CONFIG', useValue: CONFIG }],
})
export class AppModule {}

Factory Provider

A factory provider tells the IoC container to use a factory function to create the dependency. This is useful for complex initialization logic or when dependencies need to be created dynamically.

Purpose

  • To provide complex or dynamic instances.
  • To run custom initialization logic.
import { NsModule, Injectable } from '@nexus-ioc/core';
import { uid } from "uid";

@NsModule({
  providers: [
    {
      provide: 'UID_GENERATOR',
      useFactory: () => uid(),
    },
  ],
})
export class AppModule {}

Inject Decorator

The @Inject() decorator is a key feature in Nexus IoC for injecting dependencies. It can be used to explicitly define which provider to inject, ensuring that even if multiple providers exist, the correct one is used. The @Inject() decorator can be applied to both constructor parameters and class properties.

Purpose

To explicitly inject a specific dependency when multiple providers or tokens exist. To inject a dependency at the property level or constructor level.

Usage in Constructor

The most common use case for @Inject() is in the constructor, where it is used to inject dependencies when a class is instantiated by the IoC container. This is useful when you need to inject a value or a specific provider by token.

import { Injectable, Inject, NsModule } from '@nexus-ioc/core';

@Injectable()
class AppService {
  constructor(@Inject('CONFIG') private config: any) {}

  getConfig(): any {
    return this.config;
  }
}

@NsModule({
  providers: [
    { provide: 'CONFIG', useValue: { apiUrl: 'https://api.example.com' } }
  ],
})
export class AppModule {}

In this example, the @Inject('CONFIG') decorator tells Nexus IoC to inject the CONFIG value when the AppService is instantiated. This ensures that the correct value is passed into the constructor.

Usage in Properties

In addition to constructor injection, Nexus IoC allows property-based injection. The @Inject() decorator can be applied directly to class properties, allowing dependencies to be injected after the object has been instantiated. This can be useful when you want to avoid passing dependencies through the constructor or when dependencies need to be set after object creation.

import { Injectable, Inject, NsModule } from '@nexus-ioc/core';

@Injectable()
class AppService {
  @Inject('CONFIG')
  private config: any;

  getConfig(): any {
    return this.config;
  }
}

@NsModule({
  providers: [
    { provide: 'CONFIG', useValue: { apiUrl: 'https://api.example.com' } }
  ],
})
export class AppModule {}

Here, the @Inject('CONFIG') decorator is used directly on the config property, allowing Nexus IoC to inject the dependency after the object is created.

Optional Decorator

The @Optional() decorator in Nexus IoC is used when a dependency might not be available in the IoC container. It allows the injection of an optional dependency, meaning that if the provider is not found, the IoC container will not throw an error. Instead, it will inject undefined

Purpose

  • To make a dependency optional, avoiding errors when the provider is not available.
  • To provide flexibility in situations where certain components or services are not always required.

Usage in Constructor

When using @Optional() in the constructor, it allows the container to inject undefined if the dependency is not registered in the container.

import { Injectable, Optional, Inject, Module } from '@nexus-ioc/core';

@Injectable()
class LoggerService {
  log(message: string): void {
    console.log(message);
  }
}

@Injectable()
class AppService {
  constructor(
    @Optional() @Inject(LoggerService) private readonly logger?: LoggerService // Dependency might not exist
  ) {}

  run(): void {
    if (this.logger) {
      this.logger.log('App started');
    } else {
      console.log('LoggerService is not available');
    }
  }
}

@Module({
  providers: [AppService],  // LoggerService is not provided
})
export class AppModule {}

In this example, the LoggerService is marked as optional in the constructor. Since LoggerService is not provided in the AppModule, the IoC container will inject undefined, and the AppService will run without the logger.

Usage in Properties

You can also use the @Optional() decorator on properties, allowing the injection of optional dependencies after the object has been instantiated.

import { Injectable, Optional, Inject, Module } from '@nexus-ioc/core';

@Injectable()
class AppService {
  @Optional()
  @Inject(LoggerService)
  private readonly logger?: LoggerService;

  run(): void {
    if (this.logger) {
      this.logger.log('App started');
    } else {
      console.log('LoggerService is not available');
    }
  }
}

@Module({
  providers: [AppService],  // LoggerService is not provided
})
export class AppModule {}

In this case, the logger property will be undefined if LoggerService is not registered in the container.