I'am trying to use AuthService into UsersService and UsersService into AuthService, so this is called "circular dependency". The issue is that "Nest can't resolve dependencies of the AuthService (UserModel, JwtService, ?). Please make sure that the argument dependency at index [2] is available in the AuthModule context."
UsersModule:
#Module({
imports: [
MongooseModule.forFeature([
{
name: User.name, schema: UserSchema
}
]),
forwardRef(() => AuthModule),
],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService]
})
export class UsersModule {}enter code here
AuthModule:
#Module({
imports: [
MongooseModule.forFeature([{name: User.name, schema: UserSchema}]),
JwtModule.register({ secret: process.env.JWT_SECRET }),
forwardRef(() => UsersModule),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService, JwtModule]
})
export class AuthModule {}
UsersService (works fine):
#Injectable()
export class UsersService {
constructor(
#InjectModel(User.name) private userModel: Model<UserDocument>,
private jwtService: JwtService,
private authService: AuthService
) {}
...
AuthService (where error occurs):
#Injectable()
export class AuthService {
constructor(
#InjectModel(User.name) private userModel: Model<UserDocument>,
private jwtService: JwtService,
private userService: UsersService,
) {}
...
You've resolved the circular dependency between the modules, but not between the services. Each side of the service needs #Inject(forwardRef(() => InjectedClass)). So your AuthService would use #Inject(forwardRef(() => UserService))
Circular dependencies are bad and trying to get around them using things like forwardRef mentioned in the other answer is not good practice.
Look into other patterns for inter-service communication. Events for example: https://docs.nestjs.com/techniques/events
Related
I have a circular dependency like so
Auth.service.ts
export class AuthService {
constructor(
#Inject(forwardRef(() => UserService))
private userService: UserService,
)
User.service.ts
export class UserService {
constructor(
#Inject(forwardRef(() => AuthService))
private authService: AuthService,
)
I have 3 controllers where I access these.
User.controller.ts
export class UserController {
constructor(private readonly userService: UserService) {})
Auth.controller.ts
export class AuthController {
constructor(private readonly authService: AuthService) {})
AuthUser.controller.ts
export class AuthUserController {
constructor(
private readonly authService: AuthService,
private readonly userService: UserService,
) {}
I have a module.ts file where I'm setting everything up
#Module({
imports: [],
controllers: [
AuthUserController,
AuthController,
UserController,
],
providers: [
AuthService,
UserService,
],
I have a few endpoints in my controllers. They work fine from AuthController & UserController. But when calling from AuthUserController the classes are not being instantiated in the code. I'm not sure why or what I'm missing
I have two modules whose services are dependent on one another.
// organization.module.ts
//...imports
#Module({
imports: [
forwardRef(() => EmployeeModule),
TypeOrmModule.forFeature([
OrganizationRepository,
Employee
])
],
controllers: [OrganizationController],
providers: [
OrganizationService
]
})
export class OrganizationModule {}
Organization Service*
#Injectable()
export class OrganizationService {
constructor(
#InjectRepository(OrganizationRepository)
private orgRepository: OrganizationRepository,
#InjectRepository(Employee)
private employeeRepsoitory: Repository<Employee>,
private readonly employeeService: EmployeeService,
){}
//...
}
Then i have my employee module and service
employee.module.ts
#Module({
imports: [
forwardRef(() => OrganizationModule),
//...
]
controllers: [EmployeeController],
providers: [EmployeeService, JwtStrategy],
exports: [EmployeeService],
})
export class EmployeeModule {}
and then i have my employee service
employee.service.ts
#Injectable()
export class EmployeeService {
constructor(
private readonly organizationService: OrganizationService
) {}
Now i am getting this DI error and i dont know what is going wrong here. I have tried
#Inject(forwardRef(() => Service)) but didnt seem to work.
My app.module.ts looks like
//...
#Module({
imports: [
OrganizationModule,
],
controllers: [AppController],
providers: [
AppService,
],
})
export class AppModule {}
What am i doing wrong?
You are missing an exports in OrganizationModule.
Add to OrganizationModule:
exports: [OrganizationService]
I am writing end-to-end tests in NestJs and using the "overrideProvider" function to setup test versions of various services (e.g. services that require database connections). I noticed though that even when I do this, the original implementation that injects the real database is still instantiated.
Is there a way to tell Nest to not create transitive dependencies that are overridden?
For example, I have a test that starts like:
...
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [ServiceModule],
})
// Works if I uncomment these lines:
// .overrideProvider('Database')
// .useValue(new TestDatabase())
.overrideProvider('ServiceUsingDatabase')
.useValue(new TestService())
.compile();
...
Where the module setup is like:
import { Inject, Injectable, Module } from '#nestjs/common';
interface Database {}
#Injectable()
class ProductionDatabase implements Database {
constructor() {
throw('Cannot create a production database.');
}
}
#Injectable()
export class TestDatabase implements Database {
constructor() {
console.log('Creating the test database.');
}
}
#Module({
providers: [
{
provide: 'Database',
useClass: ProductionDatabase
}
],
exports: ['Database']
})
class DatabaseModule {}
interface Service {}
#Injectable()
class ProductionService implements Service {
constructor(#Inject('Database') private readonly database: Database) {}
}
#Injectable()
export class TestService implements Service {
// Test implementation of the service does not Inject anything.
}
#Module({
imports: [DatabaseModule],
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: ProductionService
}
],
})
export class ServiceModule {}
But, the DI system is still seeming to try and instantiate ProductionDatabase. If I explicitly override the provider for the 'Database' it works, but I'd like to avoid having to explicitly list all transitive dependencies as such.
I ended up deciding to make a "Test" Module for every Module e.g.:
import { Inject, Injectable, Module } from '#nestjs/common';
interface Database {}
#Injectable()
class ProductionDatabase implements Database {
}
#Injectable()
export class TestDatabase implements Database {
}
#Module({
providers: [
{
provide: 'Database',
useClass: ProductionDatabase
}
],
exports: ['Database']
})
class DatabaseModule {}
#Module({
providers: [
{
provide: 'Database',
useClass: TestDatabase
}
],
exports: ['Database']
})
class TestDatabaseModule {}
interface Service {}
#Injectable()
class ProductionService implements Service {
constructor(#Inject('Database') private readonly database: Database) {}
}
#Injectable()
export class TestService implements Service {
}
#Module({
imports: [DatabaseModule],
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: ProductionService
}
],
})
export class ServiceModule {}
#Module({
providers: [
{
provide: 'ServiceUsingDatabase',
useClass: TestService
}
],
})
export class TestServiceModule {}
etc... Though it turned out after some refactorings that the "Test" module wasn't needed as some Modules became pure business logic.
enter image description here
I have been having a dependency problem that is solved by injecting the service and using forwardRef, but this generates a strange error and the service responds only once and then the dependencies are lost, example: when calling the service the first time it returns the response, when trying to consume it again I get this error "Cannot read property 'axiosRef' of undefined" as if the HttpService was no longer available.
enter image description here
auth0.module.ts
#Module({
imports: [HttpModule, forwardRef(() => UsersModule)],
controllers: [Auth0Controller],
providers: [Auth0Service, ConfigService],
})
export class Auth0Module {}
auth0.service.ts
export class Auth0Service {
constructor(
#Inject(forwardRef(() => UsersService))
private usersService: UsersService,
private httpService: HttpService,
private configService: ConfigService,
) {}
users.module.ts
#Module({
imports: [
DatabaseModule,
HttpModule,
forwardRef(() => SquareModule),
TenantModule,
],
controllers: [UsersController],
providers: [UsersService, ...usersProviders],
exports: [...usersProviders, UsersService],
})
export class UsersModule {}
users.service.ts
export class UsersService {
constructor(
private squareService: SquareService,
#Inject(REQUEST) private request: Request,
#Inject('USER_MODEL') private usersModel: Model<any>,
) {}
square.module.ts
#Module({
imports: [
HttpModule,
forwardRef(() => UsersModule),
forwardRef(() => TenantModule),
],
controllers: [SquareController],
providers: [
SquareService,
ConfigService,
SDKSquareService,
CryptoService,
Auth0Service,
TokenService,
],
exports: [
SquareService,
ConfigService,
SDKSquareService,
CryptoService,
Auth0Service,
],
})
export class SquareModule {}
square.service.ts
export class SquareService {
constructor(
private configService: ConfigService,
private sdkSquareService: SDKSquareService,
private tenantService: TenantService,
private auth0Service: Auth0Service,
private tokenService: TokenService,
private httpService: HttpService,
#Inject('USER_MODEL') private userModel: Model<any>,
#Inject('TENANT_MODEL') private tenantModel: Model<any>,
) {}
I am using #nestjs/jwt in my NestJS project.
I have two modules, AuthModule and AppModule.
The AuthModule uses the #nestjs/jwt
The AppModule invoke auth service from AuthModule.
AuthService:
import { Injectable } from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { ForbiddenException } from '#nestjs/common';
#Injectable()
export class AuthService {
constructor(private readonly jwt: JwtService) {}
async validate(token: string) {
return this.jwt.verify(token);
}
...
}
AuthModule:
import { Module } from '#nestjs/common'
import { TokenService } from './auth.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { JwtModule } from '#nestjs/jwt'
#Module({
imports: [
JwtModule.register({
secret: "mysecret",
signOptions: { expiresIn: "60s" },
}),
],
// I provide AuthService & JwtService
providers: [AuthService, JwtService],
// I export AuthService and JwtService
exports: [AuthService, JwtService],
})
export class AuthModule {}
AppModule:
#Module({
imports: [
AuthModule,
...
],
controllers: [AppController],
// I provide AuthService & JwtService also in AppModule
providers: [AppService, JwtService],
})
export class AppModule {}
(The AppController invokes AuthService instance to validate token.)
I constantly get error:
Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0]
Why is that? Where do I miss?
You don't need to add JwtService to the providers. Nest will look through the current module's providers, then the imported module's exports, and then the global module's exports to find providers that are not immediately in the level it's resolving the providers in. This means that for AuthService it'll look up to the JwtModule's exports and find the JwtService already created, and use that instance. Then in the AppModule you don't need to mention the AuthService or JwtService, just import the AuthModule that exports the AuthService and JwtModule (doing some module re-exporting) and you should be good to go
EDIT: Added module code:
AuthModule
import { Module } from '#nestjs/common'
import { TokenService } from './auth.service'
import { ConfigModule, ConfigService } from '#nestjs/config'
import { JwtModule } from '#nestjs/jwt'
#Module({
imports: [
JwtModule.register({
secret: "mysecret",
signOptions: { expiresIn: "60s" },
}),
],
providers: [AuthService],
exports: [AuthService, JwtModule],
})
export class AuthModule {}
AppModule
#Module({
imports: [
AuthModule,
...
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}