#Inject services in other service - Nest can't resolve dependencies - node.js

I'm trying to #Inject two services into a NestJS service and am getting
Nest can't resolve dependencies of the MainServiceImpl (?,
Symbol(MainDao)). Please make sure that the argument at index [0] is
available in the MainModule context
Here is the service:
#Injectable()
export class MainServiceImpl implements MainService {
constructor(
#Inject(TYPES.PublishSubscriptionService) private publishSubscriptionService: PublishSubscriptionService,
#Inject(TYPES.MainDao) private mainDao: MainDao
) {}
Now if I switch the order, it's always the second injected service that gets the error.
TYPES is an object of Symbols
const TYPES = {
PublishSubscriptionService: Symbol('PublishSubscriptionService'),
MainDao: Symbol('MainDao'),
};
export default TYPES;
Here is the index.ts which is using barreling
// start:ng42.barrel
export * from './main.dao.mongo-impl';
export * from './main.dao';
export * from './main';
export * from './main.schema';
export * from './main.service.impl';
export * from './main.service';
// end:ng42.barrel
What am I missing?
Closed NestJS issue with no resolution
NestJS doc on #Global modules

I can't say for sure without seeing your MainServieImplModule or whatever the module that contains this service is called, but if I had to guess, you are not declaring your providers to Nest correctly for these two services. You'll need to create a custom provider like so
#Module({
provides: [
{
provide: TYPES.PublishSubscriptionService,
useClass: PublishSubscriptionService,
},
{
provide: TYPES.MainDao,
useClass: MainDao
},
MainServiceImpl
]
})
export class MainServiceImplModule {}
If however, those providers are part of a different module you will need to make sure that the providers are custom (as above), but are also exported so they can be imported and used in a different module. Without seeing more of your code, the question is not possible to answer, but I think this should give you the direction you need. If not, please edit your question and add more information.

I don't know what TYPES is, you can use #Inject(PublishSubscriptionService) or you can use simply:
constructor(
private publishSubscriptionService: PublishSubscriptionService,
private mainDao: MainDao
) {}

Related

NestJs -Pass in path to route handler via Dynamic Module

Im working on a team that has a bunch of services so we have a npm package that contains code shared between the services.
We have a Health check module that sets the path to globalPrefix/health. Im attempting to make this value configurable in a maintainable way.
#Injectable()
#Controller()
export class HealthController {
private readonly healthCheckOptions: HealthConfigurationOptions;
private readonly url: string;
constructor(
#Inject('CONFIGURATION_OPTIONS') private options: HealthConfigurationOptions,
private readonly healthService: HealthService,
) {
this.healthCheckOptions = options || {}
this.url = options.url
}
#Get(this.url)
async healthHandler(): Promise<HealthDto | TmoHttpException> {
return this.healthService.getStatus();
}
}
My idea was to create a Dynamic Module that can take a path as an option. In the example above There is a Dynamic Health Module that accepts an options object. But it seems like during compilation the route handler is set before the class is constructed meaning that i cannot use this.url like #Get(this.url) because there is no this yet.
At this point Im a bit stumped and haven't found anything online doing what I need.
Reflect.defineMetadata(PATH_METADATA, 'my_custom_path', MyController);
while registering your custom dynamic module will change the path of your controller. however there are still issues with this approach.
see here: https://github.com/nestjs/nest/issues/1438#issuecomment-1324011241

Proper way to manually instantiate Nest.js providers

I think I might be misunderstanding Nest.js's IoC container, or maybe DI as a whole.
I have a class, JSONDatabase, that I want to instantiate myself based on some config value (can either be JSON or SQL).
My DatabaseService provider:
constructor(common: CommonService, logger: LoggerService) {
// eslint-disable-next-line prettier/prettier
const databaseType: DatabaseType = common.serverConfig.dbType as DatabaseType;
if (databaseType === DatabaseType.JSON) {
this.loadDatabase<JSONDatabase>(new JSONDatabase());
} else if (databaseType === DatabaseType.SQL) {
this.loadDatabase<SQLDatabase>(new SQLDatabase());
} else {
logger.error('Unknown database type.');
}
}
My JSONDatabase class:
export class JSONDatabase implements IDatabase {
dbType = DatabaseType.JSON;
constructor(logger: LoggerService, io: IOService) {
logger.log(`Doing something...`)
}
}
However, the problem with this is that if I want my JSONDatabase to take advantage of injection, ie. it requires both IOService and LoggerService, I need to add the parameters from the DatabaseService constructor rather than inject them through Nest's IoC containers.
Expected 2 arguments, but got 0 [ts(2554)]
json.database.ts(7, 15): An argument for 'logger' was not provided.
Is this the proper way to do this? I feel like manually passing these references through is incorrect, and I should use Nest's custom providers, however, I don't really understand the Nest docs on this subject. I essentially want to be able to new JSONDatabase() without having to pass in references into the constructor and have the Nest.js IoC container inject the existing singletons already (runtime dependency injection?).
I might be completely off base with my thinking here, but I haven't used Nest all that much, so I'm mostly working off of instinct. Any help is appreciated.
The issue you have right now is because you are instantiating JSONDatabase manually when you call new JSONDatabase() not leveraging the DI provided by NestJS. Since the constructor expects 2 arguments (LoggerService, and IOService) and you are providing none, it fails with the message
Expected 2 arguments, but got 0 [ts(2554)]
I think depending on your use case you can try a couple of different options
If you fetch your configuration on startup and set the database once in the application lifetime you can use use a Custom provider with the useFactory syntax.
const providers = [
{
provide: DatabaseService,
useFactory: (logger: LoggerService, io: IOService, config: YourConfigService): IDatabase => {
if (config.databaseType === DatabaseType.JSON) {
return new JSONDatabase(logger, io);
} else if (databaseType === DatabaseType.SQL) {
return new SQLDatabase(logger, io);
} else {
logger.error('Unknown database type.');
}
},
inject: [LoggerService, IOService, YourConfigService]
},
];
#Module({
providers,
exports: providers
})
export class YourModule {}
If you have LoggerService, IOService and YourConfigurationService annotated with #Injectable() NestJS will inject them in the useFactory context. There you can check the databaseType and manually instantiate the correct IDatabase implementation. The drawback with this approach is that you can't easily change the database in runtime. (This might work just fine for your use case)
You can use strategy/factory pattern to get the correct implementation based on a type. Let say you have a method that saves to different databases based on an specific parameter.
#Injectable()
export class SomeService {
constructor(private readonly databaseFactory: DatabaseFactory){}
method(objectToSave: Object, type: DatabaseType) {
databaseFactory.getService(type).save(objectToSave);
}
}
#Injectable()
export class DatabaseFactory {
constructor(private readonly moduleRef: ModuleRef) {}
getService(type: DatabaseType): IDatabase {
this.moduleRef.get(`${type}Database`);
}
}
The idea of the code above is, based on the database type, get the correct singleton from NestJS scope. This way it's easy to add a new database if you want - just add a new type and it's implementation. (and your code can handle multiple databases at the same time!)
I also believe you can simply pass the already injected LoggerService and IOService to the DatabasesService you create manually (You would need to add IOService as a dependency of DatabaseServce
#Injectable()
export class DatabaseService {
constructor(common: CommonService, logger: LoggerService, ioService: IOService) {
// eslint-disable-next-line prettier/prettier
const databaseType: DatabaseType = common.serverConfig.dbType as DatabaseType;
if (databaseType === DatabaseType.JSON) {
this.loadDatabase<JSONDatabase>(new JSONDatabase(logger, ioService));
} else if (databaseType === DatabaseType.SQL) {
this.loadDatabase<SQLDatabase>(new SQLDatabase(logger, ioService));
} else {
logger.error('Unknown database type.');
}
}
}

NestJS dynamic module registration

I have a question regarding dynamic modules importing.
Let's say I have #Injectable service which depends on the parameter that came from the configuration:
#Injectable()
export class StatsService {
constructor(#Inject(SERVICE_NAME) private serviceName: string) {}
...
}
Let's say I expose this service through the Dynamic module:
#Module({})
export class CoreServicesModule {
static register(coreConfig: CoreServicesConfig): DynamicModule {
const { serviceName } = coreConfig;
return {
module: CoreServicesModule,
providers: [
{
provide: SERVICE_NAME,
useValue: serviceName
},
StatsService
],
exports: [
StatsService
]
};
}
}
Let's say my application is pretty big, and I have a lot of different modules.
But I need StatsService in every one of them.
So for example to be able to use StatsService in one of the modules i need to do something like:
#Module({
imports: [CoreServicesModule.register({ serviceName: 'test', ... })]
})
export class SomeModule { ... }
And I need to do it for each module...
Do we have a way to do it only once in NestJS?
Or how we can re-use the already registered module?
#Global decorator also not help me here cause I still need to import the module in each place...
#Global() decorator will help you if you are interested in using StatService in all other modules without the need to import that module!
BUT from your code it seems that each module that import CoreServicesModule would also make a small adjustment to it by dynamically specifying the providers it has (such as ValidationPipe).
in that case you should NOT make it #Global() since global modules should be registered only once

NestJS inject custom TypeOrm repository based on an Interface

I'm currently working through the database integration docs for NestJS using TypeOrm. In these docs there are examples that show how to inject a custom database repository using the app.module from NestJS. All of these examples inject classes using the actual type of the custom repository.
#Injectable()
export class AuthorService {
constructor(private authorRepository: AuthorRepository) {}
}
This code is injected via the app.modules by providing a import like such:
#Module({
imports: [TypeOrmModule.forFeature([AuthorRepository])],
controller: [AuthorController],
providers: [AuthorService],
})
export class AuthorModule {}
This works well if you are fine with programming against an implementation, but I prefer to use an interface in my classes. I've already found the solution to injecting classes via an interface with NestJS in a previous question, but when I try to inject my custom repository like that, it doesn't seem to instanciate correctly and becomes undefined.
(node:16658) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'save' of undefined
Because of this, I assume you can only inject customRepositories via the forFeature() call in the app.module, but that won't allow me to use interfaces for injection, as far as I know. Is there any other way I can inject a custom TypeOrm repository without having the replace all my interfaces for the implementation of my custom repository? Thanks in advance!
Edit
Here is my current code, I managed to get it to inject, but this still forces me to use the implementation instead of the interface each time I call the constructor. This is mainly an issue when testing due to mocking.
#CommandHandler(FooCommand)
export class FooHandler
implements ICommandHandler<FooCommand> {
private fooRepository: IFooRepository; // Using Interface as a private property.
private barEventBus: IEventBus;
constructor(fooRepository: FooRepository,
barEventBus: EventBus) { // Forced to use implementation in constructor for injection.
this.fooRepository = fooRepository;
this.barEventBus = barEventBus;
}
#EntityRepository(FooEntity)
export class FooRepository extends Repository<FooEntity> implements IFooRepository {
getFoo() {
// Do stuff
}
}
#Module({
imports: [TypeOrmModule.forRoot(), TypeOrmModule.forFeature([FooRepository]],
// Other module setup
})
export class AppModule {}
It should work with using the InjectRepository decorator where you specify the Repository but then you type is as your interface instead and when testing you just provide the IFooRepository!
Example code:
constructor(#InjectRepository(FooRepository) fooRepository: IFooRepository,
barEventBus: EventBus) {
Edit: This answer is crap, that abstract-class-as-interface hack used does not work out as the defined methods seem to be optional to implement despite being marked as abstract.
Well, kind of got it working. Based on this answer https://stackoverflow.com/a/74561702/913136 I used an abstract class as interface (you can actually implement it) for not being required to pass strings around as tokens. Only drawback is the misuse of the abstract class. Not sure yet if I like it.
Using an actual interface in the same way seems not to be possible unfortunately. Urgh.
#Module({
imports: [
TypeOrmModule.forRoot({
...dataSource.options,
autoLoadEntities: true,
}),
TypeOrmModule.forFeature([Listing]),
],
controllers: [ViewListingController],
providers: [
{
provide: ListingRepository,
useClass: TypeOrmListingRepository,
},
],
})
makeshift interface:
import { Listing } from "./Listing";
export abstract class ListingRepository {
abstract findMostRecent: () => Promise<Listing[]>;
}
implementation:
import { Listing, ListingRepository } from "../../Domain";
import { Injectable } from "#nestjs/common";
import { Repository, DataSource } from "typeorm";
#Injectable()
export class TypeOrmListingRepository
extends Repository<Listing>
implements ListingRepository
{
constructor(private dataSource: DataSource) {
super(Listing, dataSource.createEntityManager());
}
findMostRecent() {
return this.find({});
}
}
import { Controller, Get } from "#nestjs/common";
import { ListingRepository } from "../Domain";
#Controller("listings")
export class ViewListingController {
constructor(private readonly listingRepo: ListingRepository) {}
#Get("/most-recent")
listMostRecent() {
return this.listingRepo.findMostRecent();
}
}

Nest can't resolve dependencies of AuthService

I am following the documentation present here
https://docs.nestjs.com/techniques/authentication#jwt-functionality
To have faster support I have created a git repository with the problem
https://github.com/Sano123456/nestjs-jwt
node -v -> v10.15.2
npm -v -> 6.14.6
nest -v -> 7.4.1
First Problem:
in AuthModule if I do as described in documentation and just import UserModule, it return me error of circular dependency between UserModule and AuthModule
#Module({
imports:[
UsersModule,
PassportModule
],
providers: [AuthService],
controllers: [AuthController],
exports:[AuthService, LocalStrategy]
})
export class AuthModule {}
error:
[ExceptionHandler] Nest cannot create the AuthModule instance.
The module at index [0] of the AuthModule "imports" array is undefined.
Potential causes:
- A circular dependency between modules. Use forwardRef() to avoid it. Read more: https://docs.nestjs.com/fundamentals/circular-dependency
- The module at index [0] is of type "undefined". Check your import statements and the type of the module.
Scope [AppModule -> UsersModule] +6ms
Possibile solution in imports array of AuthModule instead of UserModule put forwardRef(() => UsersModule),
this actually remove the error but not sure if this is the right way
Second problem:
it says that can't find LocalStrategy class even if it's present and declared in AuthModule
[ExceptionHandler] Nest cannot export a provider/module that is not a part of the currently processed module (AuthModule). Please verify whether the exported LocalStrategy is available in this particular context.Is LocalStrategy part of the relevant providers/imports within AuthModule?
Possibile solution right now I don't have any solution, I just remove it to understand what is the problem
Third problem :
after removing LocalStrategy,
[ExceptionHandler] Nest can't resolve dependencies of the AuthService (?). Please make sure that the argument dependency at index [0] is available in the AuthModule context.
Potential solutions:
- If dependency is a provider, is it part of the current AuthModule?
- If dependency is exported from a separate #Module, is that module imported within AuthModule?
#Module({
imports: [ /* the Module containing dependency */ ]
})
+1ms
Error: Nest can't resolve dependencies of the AuthService (?). Please make sure that the argument dependency at index [0] is available in the AuthModule context.
anyone solved this issues?
You have a circular dependancy because your UsersModule and your AuthModule import each other. Therefore you have 2 options to repair this issue.
The first is to make a forward reference which will allow both modules to be built simultaneously and once built, pass in the reference required. This is done like so:
#Module({
imports: [
forwardRef(() => UsersModule),
],
...
})
export class AuthModule {}
// And the same for your UsersModule
#Module({
imports: [
forwardRef(() => AuthModule),
],
})
export class UsersModule {}
The second option is to remove the dependancies from one another. This is not always possible and guessing by the names of your modules I would argue this isn't possible. You don't want to have the auth module accessing the user module's database and services etc outside the users module. But I'll give you some advice on module inheritance.
Module in nestjs are built asynchronously. Meaning they have to be structured like a cascading tree with one module importing all the others. Something linear will look like this
AppModule <= CatModule <= PawsModule
Another example would be dual importing
<= CatModule
AppModule <= PawsModule
<= DogModule
In the above example, the paws module is created once and imported in both cat and dog module. Meaning paws module will be built first before cat and dog module.
So to explain your error you need to think from a linear code perspective and how the module import tree will share modules that import each other. Because they import each other, nest can't decide which to create first so will need a reference to pass back, hence the function. I've always imagined the container's forward ref function as saying "Hey, hold this for a sec" (it's a piece of paper that says "UsersModule") then turns around, does some wavey of the arms with his back turned, then turns around and replaces the piece of paper with the UsersModule!
Your second issue is you never created a provider for LocalStrategy. It doesn't exist in the AuthModule so cannot be imported into AuthService nor exported from AuthModule!
** SOLUTION of problem "can't resolve dependencies" **
For the first and second problem the final solution is to use #Inject(forwardRef(() => AuthService)) in UserService
here is example
#Injectable()
export class UserService {
constructor(
#InjectRepository(User) private readonly UserRepository: Repository<User>,
private readonly config: ConfigService,
#Inject(forwardRef(() => AuthService)) //<---
private readonly authService: AuthService,
) {
}
Same thing also in AuthService
#Injectable()
export class AuthService {
private client: any;
constructor(
#Inject(forwardRef(() => UserService))//<--- here
private readonly userService: UserService,
#InjectConfig() private readonly config,
) {
}
In AuthModule and UserModule you need to still use forwardRef
I never used this solution before and i never needed it, but this solved my issue
Regarding LocalStrategy, put it in providers array of the module
For me, it wasn't enough to just use the forwardRef on both services as in Sano's answer; I also had to forwardRef on both circular dependent modules too.
In both services:
https://docs.nestjs.com/fundamentals/circular-dependency#forward-reference
import { forwardRef, Inject } from '#nestjs/common';
...
#Inject(forwardRef(() => UserService))`
In both modules:
https://docs.nestjs.com/fundamentals/circular-dependency#module-forward-reference
import { forwardRef } from '#nestjs/common';
...
imports: [
forwardRef(() => UsersModule),
],

Resources