Skip to content

Instantly share code, notes, and snippets.

@MirzaLeka
Last active April 30, 2023 18:44
Show Gist options
  • Save MirzaLeka/7f9164192c630cdc9f911b783f3c31f2 to your computer and use it in GitHub Desktop.
Save MirzaLeka/7f9164192c630cdc9f911b783f3c31f2 to your computer and use it in GitHub Desktop.
Nest.js Depdendency Inversion example

Nest.js Dependency Inversion with Abstract classes

The Motivation

In strictly typed object-oriented languages such as Java or C#, it's a common practice to invoke a method from a service by calling an interface as opposed to an actual service. This pattern is useful when swapping dependencies on the fly (like logging to the console or to the database) or unit testing, as we're not bounded to work with actual classes, but rather abstractions.

The Problem

Nest.js however does not allow for using interfaces as providers because injection tokens only work strings, symbols and classes (as explained here). However, it does work with Abstract classes which we'll use here.

img

Let's get started

Generate Abstract class

export default abstract class WebFramework {
  abstract getHello(): string;
}

Generate Two Services (React & Angular)

Each service extends the abstract class above.

import { Injectable } from '@nestjs/common';
import WebFramework from '../WebFramework.class';

@Injectable()
export class AngularService extends WebFramework {
  getHello(): string {
    return 'Hello Angular!';
  }
}
import { Injectable } from '@nestjs/common';
import WebFramework from '../WebFramework.class';

@Injectable()
export class ReactService extends WebFramework {
  getHello(): string {
    return 'Hello React!';
  }
}

Setup Appmodule

In AppModule we'll inject our services using the abstract class as a provider.

import { ReactService } from './services/react/react.service';
import { AngularService } from './services/angular/angular.service';
import WebFramework from './services/WebFramework.class';

@Module({
  imports: [],
  controllers: [AppController, CoffeesController],
  providers: [
    AppService,
    {
      provide: WebFramework, // our custom injection token
      useClass: React or Angular Service,
    },
  ],
})
export class AppModule {}

Setup AppController

Here we have to use @Inject() when importing dependencies as we're using a custom injection token.

import { Controller, Get, Inject } from '@nestjs/common';
import WebFramework from './services/WebFramework.class';

@Controller()
export class AppController {
  constructor(
    @Inject(WebFramework) private readonly webFramework: WebFramework,
  ) {}

  @Get()
  getHello(): string {
    return this.webFramework.getHello();
  }
}

The beauty here is that the consumer has no clue what is webFramework.getHello() service it is.

Let's give it a whirl!

Angular Service

import { ReactService } from './services/react/react.service';
import { AngularService } from './services/angular/angular.service';
import WebFramework from './services/WebFramework.class';

@Module({
  imports: [],
  controllers: [AppController, CoffeesController],
  providers: [
    AppService,
    {
      provide: WebFramework, // our custom injection token
      useClass: AngularService, // <--- Setting Angular Service!
    },
  ],
})
export class AppModule {}
npm run start:dev
http:localhost:3000

> Hello Angular!

React service

We just need to swap classes in AppModule

import { ReactService } from './services/react/react.service';
import { AngularService } from './services/angular/angular.service';
import WebFramework from './services/WebFramework.class';

@Module({
  imports: [],
  controllers: [AppController, CoffeesController],
  providers: [
    AppService,
    {
      provide: WebFramework,
      useClass: ReactService, // <--- Setting React Service!
    },
  ],
})
export class AppModule {}
http:localhost:3000

> Hello React!

And that's it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment