Nest.js: Circular dependencies in dynamic modules - module

What is the correct way to import dynamic modules when there's a circular dependency between them? I simply changed forwardRef(() => MyModule) to forwardRef(() => MyModule.forRoot()), and I'm getting Nest can't resolve dependencies error.

The best way to deal with nest module dependency is to import dependencies always through modules and never through services. If you run into a circular dependency, nest will show you an error and you can easily fix it with forwardRef().
let say your application has 3 tightly coupled modules.
#moduleA()
#moduleB()
#moduleC()
And two supporting modules
#moduleX()
#moduleY()
#moduleZ()
Also, all modules are exporting services with the same name.
Consider a situation where
#moduleA() imports [serviceB, serviceX]
And
#moduleB() imports [serviceA, serviceY, serviceZ]
Now in #moduleC() if you want to use serivceA() then you will have to
#moduleC() imports [serviceA, serviceB, serviceX, serviceY, serviceZ]
In most cases, nest throws the correct error saying which dependency is missing where. But some times, nest only says dependency at [index] is missing, nest doesn't says where it is missing. This will lead to great level of confusion.
A neat approach is to always import modules
#moduleA() imports [moduleB, moduleX]
#moduleB() imports [moduleA, moduleY, moduleZ]
#moduleC() imports [moduleA]
If nest is complaining again about dependency issue then import modules using forwardRef
#module({
imports: [
forwardRef(() => ModuleA)
ModuleB
],
controllers: [],
providers: []
})
Sometimes even after doing all these, you may again run into dependency not available error. Then you can use ModuleRef. Let's take the same example where you want to use ServiceA() in ModuleC()
You will then be adding serviceA() to providers[] in ModuleC() and in the serviceC() class you need to add the following.
import { Injectable, OnModuleInit } from '#nestjs/common'; \\need OnModuleInit
import { ServiceA} from '../FolderA/serviceA.service';
export class ServiceC implements OnModuleInit {
private serviceA: ServiceA;
constructor(private readonly moduleRef: ModuleRef) {}
onModuleInit() {
this.serviceA= this.moduleRef.get(ServiceA);
}
foo(){
console.log(this.serviceA.someFunction())
}
}
Please check more at Official documentation.

Related

Integrating Mollie payments in NestJS backend

I am trying to integrate Mollie payments into my NestJS backend.
To make a connection with Mollie, I have to use their MollieClient function. However, when I try to use it in a service I get the error:
Nest can't resolve dependencies of the NewUserService (UserService, MailService, ConfigService, JwtService, ?). Please make sure that the argument Object at index [4] is available in the NewUserModule context.
I am pretty sure this means I have to add a Mollie module/service to the NewUserModule, but I think the package doesn't actually come with a module made for NestJS. So if I try to make a Mollie module/service or use the MollieClient in another service, it asks me to provide it whilst I don't have anything to provide.
I'm pretty new to NestJS and backend development in general, so am I mistaken? Or is there a module added in the installed package?
If there isn't a module, should I make one? What exactly should be in such a module? Is there some sort of guide for it?
I realise this might be a rather vague series of questions, but I'm not very sure how to approach this.
Edit:
Rewrote the question for clarification.
Thanks in advance!
The message means, that Nest does not now how to resolve the 5th constructor argument of your NewUserService. I assume this is something like a MollieService?
You need to add MollieService as Provider to your NewUserModule:
#Module({
imports: [...],
controllers: [...],
providers: [
...otherProviders,
MollieService
]
})
export class NewUserModule {}
Or you can create a separate MollieModule and import it in NewUserModule:
#Module({
providers: [ MollieService ],
exports: [ MollieService ] // export MollieService, so that other modules can use it
})
export class MollieModule {}
#Module({
imports: [MollieModule],
controllers: [...],
providers: [...] // no need to provide MollieService here, because it's imported from MollieModule
})
export class NewUserModule {}
Of course you must also implement the MollieService using their SDK.
A recommend to read the Documentation on Modules. They are a little hard to understand at a first sight, but a powerful concept!
EDIT:
I would suggest to wrap the MollySDK in a service. This way you're not dealing with molly at various places in your app and prevent leaking it's api and types into your services.
#Injectable()
export class MollyService {
private readonly mollyClient: WhateverType;
constructor() {
this.mollyClient = createMollieClient({ apiKey: 'test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM' });
}
createPayment() {
this.mollieClient.payments.create({...});
}
}
This service could be injected as usual.
However if you really want to use the molly-client directly, you have to use a custom provider
#Module({
providers: [{
provides: 'MOLLY_CLIENT',
useFactory: () => createMollieClient({ apiKey: 'test_dHar4XY7LxsDOtmnkVtjNVWXLSlXsM' }) // you could also use useValue instead of useFactory
}],
exports: [ 'MOLLY_CLIENT' ]
})
export class MollieModule {}
Then inject it into NewUsersService like so:
constructor(#Inject('MOLLY_CLIENT')private readonly mollyClient: WhateverType) {}

How to init several root provided services on app initialization in Angular 8?

I have several initializable services that implement this simple interface:
interface InitializableService {
init(): void;
}
Those services make some global subscriptions (local and external) that I need during all the live of my app. What I do now is inject those services into my app.component.ts and call them one for one. I want to automate this process.
I have found some info about ways to do this and I just found ways to do it when providing the services into an module. I provide that services in root and I do not want to change this, besides the initial arrangement take the same time to just injecting them into app.components.ts.
I tried creating a base class and extend it so each class service that implement it would become "automatic initialized" on app load but I wasn't able to get the list of services that extend that class to call init on them.
I have no idea where to start to achieve this, or if this is even possible with provideIn: 'root' services.
Have you tried to use Modules with providers ?
solution is , to create a module , something like core.module.ts
then import the module into app.module.ts.
The key is , inside of core module , using module with providers , you can initial the those useful services.
Using provide in root , I just don't like it
core.module.ts Will be something like this
import { ModuleWithProviders, NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { UsersData } from './data/users';
import { UsersService } from './mock/users.service';
const DATA_SERVICES = [
{ provide: UsersData, useClass: UsersService },
];
#NgModule({
imports: [
CommonModule,
],
providers: []
})
export class CoreModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
...DATA_SERVICES,
]
} as ModuleWithProviders;
}
}

ngrx/effects unit tests using jasmine-marbles

When I try to use either cold or hot functions from jasmine-marbles, I keep getting TypeError: Class constructor Observable cannot be invoked without 'new'. Anybody know how to resolve this error? Here is what I have below.
import { hot, cold } from "jasmine-marbles";
import { Observable } from "rxjs/Observable";
const myAction = hot("--a-", { a: "TEST" };
I started to get this error after i changed "target" property in tsconfig.json (at project root) to "es6", thus i restored to old good "es5"

Aurelia Webpack loader unable to find a module I add as a feature

I have a small Aurelia app built with Webpack. Under my src folder I have util folder with index.ts inside. In main.ts I turn the feature on like this:
import { Aurelia, PLATFORM } from "aurelia-framework";
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.feature(PLATFORM.moduleName("util"));
aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName("app")));
}
util/index.ts:
import { FrameworkConfiguration } from 'aurelia-framework';
export function configure(config: FrameworkConfiguration): void {
config.globalResources([
"./converters",
"./rest"
]);
}
converters and rest are Typescript modules under util.
I'm following the instructions from Aurelia Hub.
When I open the app in the browser I see the following error:
Uncaught (in promise) Error: Unable to find module with ID: util/index
at WebpackLoader.<anonymous> (aurelia-loader-webpack.js:187)
at step (aurelia-loader-webpack.js:36)
at Object.next (aurelia-loader-webpack.js:17)
at aurelia-loader-webpack.js:11
at Promise (<anonymous>)
at webpackJsonp.64.__awaiter (aurelia-loader-webpack.js:7)
at WebpackLoader.webpackJsonp.64.WebpackLoader._import (aurelia-loader-webpack.js:152)
at WebpackLoader.<anonymous> (aurelia-loader-webpack.js:252)
at step (aurelia-loader-webpack.js:36)
at Object.next (aurelia-loader-webpack.js:17)
If I reference the modules directly instead of the feature e.g.
import { Rest } from '../util/rest';
Then I get no errors and the app loads successfully. But I want to have these modules globally available.
Using aurelia-webpack-plugin version 2.0.0-rc.2
Would appreciate your advice.
Take a look at this:
https://github.com/aurelia/templating-resources/blob/master/src/aurelia-templating-resources.js
Edit:
I have gotten it working. The key is that you need to explicitly call PLATFORM.moduleName('./relative/path/to/file') on each path specifically and that the call needs to be made from the file (actually technically the same directory but still...) that calls config.globalResources().
In other words you can't shortcut the following code:
config.globalResources(
PLATFORM.moduleName('./resource1'),
PLATFORM.moduleName('./resource2')
);
Don't try to map the resources to PLATFORM.moduleName or to dynamically construct file names.
You should change your path PLATFORM.moduleName("util") into PLATFORM.moduleName("./util"). Then you can be able to use path relative to your util folder to register your globalResources. Also you can look into here for sample project structure. Also see this line of code, aurelia added /index to moduleName if it not included as it was based on convention.
If you already try using aurelia CLI, the created project under src/resources is registered as a feature.

How to set CRUD in a subpackage with Play

I am trying to use the CRUD module in subpackages instead of the default one.
package controllers.admin;
import models.Branch;
import controllers.CRUD;
#CRUD.For(Branch.class)
public class Branches extends CRUD {
public static void index() {
render();
}
}
My routes file is:
# Import CRUD routes
* /admin module:crud
However when I use the default url: http://localhost:9000/admin/
I get the template not found error:
Template not found
The template admin/Branches/index.html does not exist.
How can I specify to CRUD module to look for views in the subpackages?
ok. controller was generated with eclipse plugin and included the index() method.
Removing it solved the problem and the module is now working correctly.