How to use provider class methods from another module in a controller in Nestjs?

January 24, 2023 - 6 min read

To use the provider class methods from another module in the current module controller,

  • First, you have to export the provider class that needs to be used in other modules by adding the provider class in the exports array and the providers array of the @Module decorator function in its module definition file.
  • Secondly, you have to import the module class that has the provider class methods to be used in the current module definition file. You can do this by adding the module class name to the imports array of the @Module decorator function in the module in which you are going to use the provider class methods.
  • Finally, you can use the imported provider class methods in the controller class methods after the initialization of the provider class.

TL;DR

Filepath: /image/image.service.ts

import { Injectable } from "@nestjs/common";

@Injectable()
export class ImageService {
  // the method we intend to use on another module
  getRandomImage() {
    return "https://picsum.photos/200";
  }
}

Filepath: /image/image.module.ts

import { Module } from "@nestjs/common";

// import the image service provider class
import { ImageService } from "./image.service";

@Module({
  imports: [],
  controllers: [],
  providers: [ImageService], // add `ImageService` provider here
  exports: [ImageService], // to export the `ImageService` class, add the class here
})
export class ImageModule {}

Filepath: /greet/greet.module.ts

import { Module } from "@nestjs/common";

import { GreetController } from "./greet.controller";

import { ImageModule } from "../image/image.module";

@Module({
  /* 
    add the `ImageModule` module here 👇🏽, so that the 
    provider class methods can be used in the current module.
  */
  imports: [ImageModule],
  controllers: [GreetController],
  providers: [],
})
export class GreetModule {}

Filepath: /greet/greet.controller.ts

import { Controller, Get } from "@nestjs/common";

import { ImageService } from "../image/image.service";

@Controller("/greet")
export class GreetController {
  // declaring and initializing the `ImageService` provider
  // class using the fast and short way in the `constructor` method itself
  constructor(private imageService: ImageService) {}

  // The `Get()` decorator function will instruct the
  // Nestjs runtime to use the `getImage()`
  // method for a `GET` of `/greet` endpoint.
  @Get()
  getImage() {
    // using the `getRandomImage()` method from the
    // instance of the `ImageService` provider class.
    return this.imageService.getRandomImage();
  }
}

Filepath: /app.module.ts

import { Module } from "@nestjs/common";

import { GreetModule } from "./greet/greet.module";

@Module({
  imports: [GreetModule], // <- add GreetModule here
})
export class AppModule {}

Confused? 🤔. See the example with 2 modules below to get a better idea.

For example, let's say we have 2 modules called image and greet in our Nestjs Application.

The image module contains a service provider class that has a method called getRandomImage() that returns the url of a random image.

The service provider class looks like this,

Filepath: /image/image.service.ts

import { Injectable } from "@nestjs/common";

@Injectable()
export class ImageService {
  // the method we intend to use on another module
  getRandomImage() {
    return "https://picsum.photos/200";
  }
}

NOTE: To know more about creating service Provider classes in Nestjs, see the blog on What is the best way to do complex business logic computations for any API in Nestjs?.

The directory structure of the image and greet modules now looks like this,

- src
  - image
  - - image.service.ts
  - greet
  - - greet.controller.ts
  - - greet.module.ts

Now let's make the image into a module by creating a class called ImageModule in a file called image.module.ts in the current directory and then use the @Module() decorator function from the @nestjs/common module.

It can be done like this,

Filepath: /image/image.module.ts

import { Module } from "@nestjs/common";

@Module({
  imports: [],
  controllers: [],
  providers: [],
  exports: [],
})
export class ImageModule {}

As you can see from the above code, we have made the image module file, but haven't exported the ImageService class that needs to be used by other modules.

To export the ImageService class, we can add the ImageService class name to the exports array as well as the providers array of the @Module() decorator function.

It can be done like this,

Filepath: /image/image.module.ts

import { Module } from "@nestjs/common";

// import the image service provider class
import { ImageService } from "./image.service";

@Module({
  imports: [],
  controllers: [],
  providers: [ImageService], // add `ImageService` provider here
  exports: [ImageService], // to export the `ImageService` class, add the class here
})
export class ImageModule {}

The directory structure of the image and greet modules now looks like this,

- src
  - image
  - - image.service.ts
  - - image.module.ts
  - greet
  - - greet.controller.ts
  - - greet.module.ts

The greet module file looks like this,

Filepath: /greet/greet.module.ts

import { Module } from "@nestjs/common";

import { GreetController } from "./greet.controller";

@Module({
  imports: [],
  controllers: [GreetController],
  providers: [],
})
export class GreetModule {}

If you look at the above code, the imports array is empty and we won't be able to use the image module's getRandomImage() method inside this module.

We aim to use the getRandomImage() method from the image module in the greet module. To do that, we have to first import the image module and add it to the imports array of the @Module() decorator function in the GreetModule class.

It can be done like this,

Filepath: /greet/greet.module.ts

import { Module } from "@nestjs/common";

import { GreetController } from "./greet.controller";

import { ImageModule } from "../image/image.module";

@Module({
  /* 
    add the `ImageModule` module here 👇🏽, so that the 
    provider class methods can be used in the current module.
  */
  imports: [ImageModule],
  controllers: [GreetController],
  providers: [],
})
export class GreetModule {}

Now to test it let's go into the getImage() method of the GreetController controller class of the greet module and use the getRandomImage() method from the ImageService class.

NOTE: We can use the method after initialization of the ImageService class in the constructor method itself.

It can be done like this,

Filepath: /greet/greet.controller.ts

import { Controller, Get } from "@nestjs/common";

import { ImageService } from "../image/image.service";

@Controller("/greet")
export class GreetController {
  // declaring and initializing the `ImageService` provider
  // class using the fast and short way in the `constructor` method itself
  constructor(private imageService: ImageService) {}

  // The `Get()` decorator function will instruct the
  // Nestjs runtime to use the `getImage()`
  // method for a `GET` of `/greet` endpoint.
  @Get()
  getImage() {
    // using the `getRandomImage()` method from the
    // instance of the `ImageService` provider class.
    return this.imageService.getRandomImage();
  }
}

Now to tie everything up, we have to add the GreetModule module class to the imports array of the app root module (app.module.ts) which can be found at the root of the src directory.

It can be done like this,

Filepath: /app.module.ts

import { Module } from "@nestjs/common";

import { GreetModule } from "./greet/greet.module";

@Module({
  imports: [GreetModule], // <- add GreetModule here
})
export class AppModule {}

Now if you go to the /greet API endpoint in the browser, you can see the image url as the response.

We have successfully integrated and used an external module provider class method in another module's controller in Nestjs. Yay 🥳!

See the above code live in codesandbox.

That's all 😃.