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>,
) {}
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
There are plenty of articles showing how to inject providers into dynamic module but all of them only show examples using their exported services, none with middleware. Using the exact same method as used with services for middleware fails. How can I create reusable middleware that requires providers to be injected?
Example middleware, requiring injection of a UserService provider
#Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(#Inject(USER_SERVICE) private readonly userService: UserService) {
console.log('userService findAll: ', userService.findAll());
}
use(req: any, res: any, next: () => void) {
console.log('AuthMiddleware called!');
next();
}
}
Example module containing the middleware:
#Module({
providers: [AuthService, AuthMiddleware],
imports: [ExtModule],
})
export class AuthModule extends createConfigurableDynamicRootModule<
AuthModule,
AuthModuleOptions
>(AUTH_OPTIONS, {
providers: [
{
provide: AUTH_SECRET,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.secret,
},
{
provide: USER_SERVICE,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.userService,
},
],
controllers: [AuthController],
}) {}
Now importing the module and trying to use the middleware:
#Module({
imports: [
ConfigModule.forRoot(),
AuthModule.forRootAsync(AuthModule, {
imports: [ConfigModule, UserModule],
inject: [ConfigService, UserService],
useFactory: (config: ConfigService, userService: UserService) => {
return {
secret: config.get('AUTH_SECRET_VALUE'),
userService,
};
},
}) as DynamicModule,
UserModule,
],
controllers: [UserController],
providers: [],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes('*');
}
}
Now when initializing the AuthModule dependencies the middleware class is clearly instantiated correctly, the userService.findAll being logged:
userService findAll: []
So far so good, and this works fine for services.
But the problem is that when then actually using the middleware in configure(), the injected context is not used
...\app\node_modules\#nestjs\core\injector\injector.js:193
throw new unknown_dependencies_exception_1.UnknownDependenciesException(wrapper.name, dependencyContext, moduleRef);
^
Error: Nest can't resolve dependencies of the class AuthMiddleware {
constructor(userService) {
this.userService = userService;
console.log('userService findAll: ', userService.findAll());
}
use(req, res, next) {
console.log('AuthMiddleware called!');
next();
}
} (?). Please make sure that the argument Symbol(USER_SERVICE) at index [0] is available in the AppModule context.
I've ended up getting it to work, mostly by re-exporting the injected providers. After trying different combinations, the working one is as follows
Static dependencies (e.g. external libraries) need to be imported and re-exported inside the module decorator
Dynamic dependencies need to be imported and re-exported inside createConfigurableDynamicRootModule
Exporting the Middleware class seems to have no effect in any way
#Module({
providers: [],
imports: [ExtModule],
exports: [ExtModule],
})
export class AuthModule extends createConfigurableDynamicRootModule<
AuthModule,
AuthModuleOptions
>(AUTH_OPTIONS, {
providers: [
{
provide: AUTH_SECRET,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.secret,
},
{
provide: USER_SERVICE,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.userService,
},
],
exports: [
{
provide: AUTH_SECRET,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.secret,
},
{
provide: USER_SERVICE,
inject: [AUTH_OPTIONS],
useFactory: (options: AuthModuleOptions) => options.userService,
},
],
}) {}
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.
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