NestJS - Inject factory provider into another provider doesn't work - javascript

I'm trying to use nest custom provider with factory, for some reason I'm struggling to get it working.
I've created the following sample -
interface ProviderOptions: {x:string, y:number};
interface AsyncProps {useFactory:(...args:any[])=> ProviderOptions, inject:any[], imports:any[] }
#Module({})
export class MyModule {
static forRootAsync(asyncProps: AsyncProps): DynamicModule {
const myFactory: Provider = {
provide: "MY_PROVIDER",
useFactory: asyncProps.useFactory,
inject: asyncProps.inject,
};
return {
module: MyModule,
providers: [myFactory, MyService],
exports: [MyService],
imports: [...asyncProps.imports],
};
}
}
#Injectable()
export class MyService {
constructor(#Inject("MY_PROVIDER") this options:ProviderOptions){
}
}
For some reason I'm unable to resolve MyService -
Error: Nest can't resolve dependencies of the MyService (?). Please
make sure that the argument dependency at index [0] is available in
the MyModule context.
what I'm missing here?
Thanks!
UPDATE -
so now it's really strange -
originally MyService was in a different file, next to MyModule. when moved MyService to the same file as MyModule, the above code does work as expected. how can it be?

Based on your error, you have a circular file import which means that typecript can't resolve the class name and in turn Nest can't create the class. I just answered another question on it here

Related

Globle module without import not working nestjs

I am trying to implement a global module in nest.js
I have created a service like below
export interface ConfigData {
DB_NAME: string;
}
#Injectable()
export class ConfigManager {
private static _instance?: ConfigManager;
public static configData:ConfigData | null;
private constructor() {
console.log(ConfigManager)
if (ConfigManager._instance)
ConfigManager._instance = this;
else{
ConfigManager._instance = new ConfigManager()
ConfigManager.configData = <ConfigData|null><unknown>ConfigManager.fetchSecretData()
}
}
private static async fetchSecretData():Promise<ConfigData|null>{
// some data from db
}
// static get instance() {
// return ConfigManager._instance ?? (ConfigManager._instance = new ConfigManager());
// //return ConfigManager._instance ?? (ConfigManager._instance = ConfigManager.fetchSecretData()) //new ConfigManager());
// }
}
configuration.module.ts
#Global()
#Module({
providers: [ConfigManager],
exports: [ConfigManager],
})
export class ConfigurationModule {}
and in app.module.ts added ConfigurationModule in imports.
Also adding private constructor on service unable it to add in module.ts file.
I am expecting that I should be able to configData anywhere without importing the ConfigManager. but it's not working...
ConfigManager is not available without import.
You've only marked your module with #Global decorator, but the NestJS needs to somehow initialize that module and make it globally available.
What this means is that you have to add this module to your core application module and NestJS will do the rest for you, so something like this (or however your root module is named):
#Module({
imports: [ConfigurationModule],
})
export class AppModule {}
From the documentation
The #Global() decorator makes the module global-scoped. Global modules
should be registered only once, generally by the root or core module.
Global modules in nest only means you don't have to include that module in the imports: [] of every other module that requires it's providers. The providers in the global module still behaves as as normal providers i.e. you need to inject it where you need it.
So in your case since you have already added #Global() to ConfigManager and imported ConfigurationModule in app.module.ts, you will not need to add ConfigurationModule in imports for any other modules who want to use ConfigManager. You would, however, still need to inject the ConfigManager provider - that's what #Injectable() means :).
To do the injection, you need a constructor(private configManager: ConfigManager) {} in the class that needs to use ConfigManager, and since you need access to the type of the class, you'll need import { ConfigManager } from '../path/to/ConfigManager' as well.
this is 'per-design' of es6/ts: you cant use the class without importing it.
you are mixing the concepts of di (instantiation/composition) with importing (defining which classes are available in the module scope)

Injection of a provider from the same module: can't resolve dependencies

Bug Report
Current behavior
Getting error while instantiating the PaymentProcessorModule:
Error: Nest can't resolve dependencies of the PaymentProcessor (?, PaymentsService, ProcessingService). Please make sure that the argument TransactionsService at index [0] is available in the PaymentProcessor context.
Potential solutions:
- If TransactionsService is a provider, is it part of the current PaymentProcessor?
- If TransactionsService is exported from a separate #Module, is that module imported within PaymentProcessor?
#Module({
imports: [ /* the Module containing TransactionsService */ ]
})
However, both services come from the same module.
Input Code
Here's my module:
#Module({
imports: [
TypeOrmModule.forFeature([ Transaction ]),
],
providers: [
PaymentProcessor,
TransactionsService,
TransactionsResolver,
],
exports: [PaymentProcessor],
})
export class PaymentProcessorModule {}
TransactionService:
#Injectable()
export class TransactionsService {
constructor(
#InjectRepository(Transaction) private transRepo: Repository<Transaction>,
) {}
//...
}
And finally, PaymentProcessor:
#Injectable()
export class PaymentProcessor {
constructor(
private transactions: TransactionsService,
private payments: PaymentsService,
private processor: ProcessingService,
) {}
//...
}
Expected behavior
Expected to inject the TransactionsService. I could not seem to reproduce it in a sample repo, unfortunately.
Environment
Nest version: 7.4.1
The official support of NestJS told me that the PaymentProcessor must be mentioned in the imports array somewhere. I checked for usages of the class and it's true, I accidentally imported the provider instead of the module in another context.

in angular, Circular dependency when using providedIn and forRoot

I am developing an angular library. it has an internal service. which is defined like below.
Used providedIn to be tree-shakable. and didn't use providedIn:'root' because its internal and just used in the module scope.
#Injectable({
providedIn: MyChartModule,
})
export class LineChartLibService {
and when i wanted to add forRoot in module definition to have just one instance in lazy loading, it encounter Circular dependency.
export class MyChartModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: MyChartModule,
providers: [LineChartLibService],
};
}
}
what should we do in this situations?
is it possible to have a lazy load-able tree-shakable service in library?
WARNING: Circular dependency: dist\my-chart\esm2015\lib\my-chart.module.js -> dist\my-chart\esm2015\lib\line-chart\line-chart.component.js
-> dist\my-chart\esm2015\lib\line-chart-lib.service.js -> dist\my-chart\esm2015\lib\my-chart.module.js
I have previously answered a (non-duplicate) question that provides you with alternatives to this approach.
https://stackoverflow.com/a/60377431/5367916
Take this setup:
my-module.ts
declarations: [
MyComponent
]
my-service.ts
#Injectable({ providedIn: MyModule })
my-component.ts
constructor(private myService: MyService) {}
my-service imports my-module
my-module imports my-component
my-component imports my-service
There is a circular dependency.
A workaround
The workaround for this is to create a service module and import that into your module.
my-module.ts
imports: [
MyModuleServices
],
declarations: [
MyComponent
]
my-module-services.ts
// no imports or declarations
my-service.ts
#Injectable({ providedIn: MyModuleServices })
my-component.ts
constructor(private myService: MyService) {}
Alternative
The much more straightforward way is to add the service to your module's providers.
#NgModule({
providers: [ MyService ]
})
export class MyModule {}
I think what happens is the following:
You have the line-chart.component.js that probably calls for the LineChartLibService.
Within the LineChartLibService you say that this Injectable is provided in MyChartModule.
Within the MyChartModule you probably declare the line-chart.component.js to be part of this module.
This means the module asks for the component, which asks for the service, which asks for the module which then asks for the component again: A circular dependency.
If you add more code and context to your question, we might be able to make a better suggestion ;-)

Move factory method from AppModule to AppComponent in Angular 7

I use ngx-translate-multi-http-loader loader to create specific translations. The documentation requires that this factory has to be created within the app.module.ts:
export function multiTranslateHttpLoaderFactory(http: HttpClient) {
return new MultiTranslateHttpLoader(http, [
{prefix: './assets/i18n/default/', suffix: '.json'},
{prefix: './assets/i18n/bc/', suffix: '.json'}
]);
}
This factory is configured inside the import arrays in this way:
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: multiTranslateHttpLoaderFactory,
deps: [HttpClient]
}
}),
I need a way to put the factory into my app.component.ts, because I still have to program some logic and do not want to do that within my app.module.ts.
But if I move the method into my app.component.ts and make all the necessary import statements on top of the file, angular doesn't compile and returns this error message:
ERROR in src / app / app.module.ts (55,21): error TS2304: Can not find
name 'multiTranslateHttpLoaderFactory'.
Is there a possibility or do I really have to define the factory within app.module.ts ???
It really should not matter where you define your factory, or any other function for that matter. Just be sure to import it correctly, somewhere in the top of app.module.ts
import {multiTranslateHttpLoaderFactory} from 'path/to/your/component'

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.
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. ⚠️
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) {}
...
}
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) {}
}
I solved my problem by removing #Inject() from the argument in my constructor that was passing the exported service.
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.
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
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.
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.

Categories

Resources