Fairydhwen
Fairydhwen

Reputation: 1835

Inject nestjs service from another module

I've got a PlayersModule and an ItemsModule.

I want to use the ItemsService in the PlayersService.

When I add it by injection:

import { Injectable } from '@nestjs/common';
import { InjectModel } from 'nestjs-typegoose';
import { ModelType, Ref } from 'typegoose';
import { Player } from './player.model';
import { Item } from '../items/item.model';
import { ItemsService } from '../items/items.service';

@Injectable()
export class PlayersService {
    constructor(
        @InjectModel(Player) private readonly playerModel: ModelType<Player>,
        private readonly itemsService: ItemsService){}

I get this nest error :

[Nest] 11592 - 2018-8-13 11:42:17 [ExceptionHandler] Nest can't resolve dependencies of the PlayersService (+, ?). Please make sure that the argument at index [1] is available in the current context.

Both modules are imported in the app.module.ts. Both services are working alone in their module.

Upvotes: 165

Views: 165226

Answers (11)

Haider Malik
Haider Malik

Reputation: 7

// src/common/common.module.ts
import { Module } from '@nestjs/common';
import { PasswordService } from '../utils/password.service';

@Module({
  providers: [PasswordService],
  exports: [PasswordService], // Export PasswordService so it can be used in other modules
})
export class CommonModule {}


// src/utils/password.service.ts
import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcrypt';

@Injectable()
export class PasswordService {
  private readonly saltRounds = 10; // This can be moved to a configuration file if needed

  async generateHash(plainText: string): Promise<string> {
    return await bcrypt.hash(plainText, this.saltRounds);
  }
}


// src/users/users.service.ts
import { Injectable, InternalServerErrorException, Logger, ConflictException } from '@nestjs/common';
import { CreateUserDTO } from './create-user.dto';
import { PrismaService } from 'src/prisma.service';
import { PasswordService } from '../utils/password.service'; // Import PasswordService

@Injectable()
export class UsersService {
  constructor(
    private prisma: PrismaService,
    private passwordService: PasswordService, // Inject PasswordService
  ) {}

  async signup(createUserDTO: CreateUserDTO) {
    try {
      const hash = await this.passwordService.generateHash(createUserDTO.password);
      createUserDTO.password = hash;

      return await this.prisma.user.create({
        data: createUserDTO,
        select: { email: true, id: true },
      });
    } catch (error) {
      
      throw new InternalServerErrorException('Signup user failed', {
        cause: new Error(),
        description: 'Error while signing up user',
      });
    }
  }
}


// src/app.module.ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { UsersService } from './users/users.service';
import { CommonModule } from './common/common.module';

@Module({
  imports: [CommonModule],
  providers: [PrismaService, UsersService],
})
export class AppModule {}

Upvotes: 0

Ponpon32
Ponpon32

Reputation: 2200

In addition to the answer of @kim-keren you may want in some cases to declare module as global instead of re-import them in each place over and over again (it is probably the best practice, but iv'e noticed many times that it cause circular dependencies).

The way to do it is first to import Global decorator from nestjs commons:

import { Global, Module } from '@nestjs/common';

Than just put it above your module definition:

@Global()
@Module( /** Module Definition **/ )
export class SomeModule { }

Upvotes: 0

Lex Nwimue P.
Lex Nwimue P.

Reputation: 9

None of the responses worked for me. Until I changed the constructor of my using/importing service from:

constructor(private usersService: UsersService) {}

to:

constructor(@Inject(UsersService) private usersService: UsersService) {}
            ^^^^^^^^^^^^^^^^^^^^^

Of course you have to import Inject, and UsersService

Upvotes: -1

PKS
PKS

Reputation: 763

Steps 1. Export the file that you want Step 2. Import the whole module.

I initially made a mistake of adding the file as provider and also adding the module which was throwing error.

Upvotes: -1

Kim Kern
Kim Kern

Reputation: 60357

You have to export the ItemsService in the module that provides it:

@Module({
  controllers: [ItemsController],
  providers: [ItemsService],
  exports: [ItemsService]
  ^^^^^^^^^^^^^^^^^^^^^^^
})
export class ItemsModule {}

and then import the exporting module in the module that uses the service:

@Module({
  controllers: [PlayersController],
  providers: [PlayersService],
  imports: [ItemsModule]
  ^^^^^^^^^^^^^^^^^^^^^^
})
export class PlayersModule {}

⚠️ Don't add the same provider to multiple modules. Export the provider, import the module. ⚠️

Upvotes: 401

Justin Dang
Justin Dang

Reputation: 374

The question is answered by Kim Kern. But I just want to remind people who read through this comment. Whenever you get this error, you should follow these steps that may help you easily figure out where the stuck is:

  • Make sure the Module which provides providers was imported.
  • Make sure the provider which you are using is exported.

For example, you have category module which contains category service, post module has post service and it has category service as a dependency:

@Module({
    controllers: [CategoryController],
    providers: [CategoryService],
    exports: [CategoryService] // Remember to export
  })
export class CategoryModule {}

And

@Module({
    imports: [CategoryModule], // Make sure you imported the module you are using
    controllers: [PostController],
    providers: [PostService]
  })
export class PostModule {}

Don't forget to use this annotation. Nest uses this to detect singleton class. In spring boot - Java, this one used to be called Bean. Read more:

@Injectable()  
export class PostService {
  constructor(private readonly categoryService: CategoryService // This will be auto injected by Nestjs Injector) {}
}

Upvotes: 20

Ivan Efremov
Ivan Efremov

Reputation: 158

Based on the answer by Kim Kern nowadays we should add only injected service into our service without any decorators (@Inject() doesn't required). After that it will work right. That was my mistake and probably can help others.

Upvotes: 2

santonil2003
santonil2003

Reputation: 656

Let' say you want to use AuthService from AuthModule in my TaskModule's controller

for that, you need to export authService from AuthModule

@Module({
    imports: [
     ....
    ],
    providers: [AuthService],
    controllers: [AuthController],
    exports:[AuthService]
  })
export class AuthModule {}
  

then in TaskModule, you need to import AuthModule (note: import AuthModule not the AuthService in TaskModule)

@Module({
    imports:[
      AuthModule
    ],
    controllers: [TasksController],
    providers: [TasksService]
  })
export class TasksModule {}

Now you should be able to use DI in TaskController

@Controller('tasks')
export class TasksController {
   constructor(private authService: AuthService) {}
   ...
}
  

Upvotes: 52

Ben Stickley
Ben Stickley

Reputation: 2120

I solved my problem by removing @Inject() from the argument in my constructor that was passing the exported service.

Upvotes: 6

peterjah
peterjah

Reputation: 11

Solved my problem by changing the way of importing the constant string (TOKEN) used in @Inject()) of my provider... be careful using index.ts whith export * from module.ts, nest won't resolve the dependecy

Upvotes: 0

Eyal Israel
Eyal Israel

Reputation: 247

I believe that you faced the same problem i had. My scenario was 2 sibling custom modules (user, auth) that needed to use each other's services. I used circular DI to solve it. please check this link

Let me know whether if it solved your issue, maybe I can advise you further.

Upvotes: 3

Related Questions