How to resolve a circular dependency in nest js? - express

I have my app.module that imports UserModule and AuthModule.
#Module({
imports: [UserModule, AuthModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
This is how my AuthModule looks:
#Module({
imports: [forwardRef(() => UserModule)],
controllers: [AuthController],
providers: [AuthService, UserService],
exports: [AuthModule],
})
export class AuthModule {}
And UserModule:
#Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
I inject userService in AuthService. If i delete AuthModule from AppModule, the dependency disapears, so, the problem is maybe somewhere there.

If you think there shouldn't be any circular dependency issue because you have follow the concept of SOLID... then you are right.. there should not be any circular dependency issue UNLESS your imports are wrong..
typescript gives facility of absolute path but always use relative path while importing
Wrong Way
import {Category} from '/src/categories';
Right Way
import {Category} from '../categories';
do this in your entire project your circular dependency will be resolved

First of all, configure the user module and auth module so that they can refer to each other.
#Module({
imports: [
forwardRef(() => UserModule),
],
exports: [AuthService],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
#Module({
imports: [
forwardRef(() => AuthModule),
],
exports: [UserService],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
In this situation, two different services that perform dependency injection must also be configured so that cross-references are possible.
#Injectable()
export class UserService {
constructor(
#Inject(forwardRef(() => AuthService))
)
}
#Injectable()
export class AuthService {
constructor(
#Inject(forwardRef(() => UserService))
)
}
Please try and comment if it doesn't work.

If you strongly believe that circular dependency is a false positive, check that if you happen to have a JSON file with the same name as a .ts file

Related

Dependency injection issue for `#ntegral/nestjs-sentry` package in nestjs app

I am have an issue with this package #ntegral/nestjs-sentry in nestjs. I have a custom logger I use in my application
#Injectable()
export class CustomLogger implements LoggerService {
constructor(#InjectSentry() private readonly client: SentryService) {}
log(message: any, ...optionalParams: any[]) {
this.client.instance().captureMessage(message, ...optionalParams);
}
}
I then inject the into User Controller and in the user.controller.spec.ts
describe('UsersController', () => {
let controller: UsersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [
CustomLogger,
UsersService,
SentryService,
],
}).compile();
controller = module.get<UsersController>(UsersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
I get this error
FAIL src/users/users.controller.spec.ts (9.449 s)
● UsersController › should be defined
Nest can't resolve dependencies of the CustomLogger (?). Please make sure that the argument Symbol(SentryToken) at index [0] is available in the RootTestModule context.
Potential solutions:
- If Symbol(SentryToken) is a provider, is it part of the current RootTestModule?
- If Symbol(SentryToken) is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing Symbol(SentryToken) */ ]
})
I have tried adding the SentryService to the spec providers but that does not fix the error. Has anyone encountered this and how did you fix it.
I run in exactly the same issue. It seemed to be that the library uses a different token for its own Inject annotation. I was able to fix it in my tests by using the provided token for the SentryService mock.
import { SENTRY_TOKEN } from '#ntegral/nestjs-sentry';
// ...
const module: TestingModule = await Test.createTestingModule({
providers: [
// ...
{
provide: SENTRY_TOKEN,
useValue: { debug: jest.fn() }, // provide SentryService Mock here
},
],
})

NestJs version for modules

I would like to add version for my modules, but i don't know how can I do this. I tried to create a common module.ts but the same name of services killed each us. I tried different module.ts for the versions, it was better but the services with same names didn't work.
This is my last structure:
test-module
1.0
controllers
test.controller.ts
services
test.service.ts
test.module.ts
1.1
controllers
test.controller.ts
services
test.service.ts
test.module.ts
This is my test service(s) for the versions:
import * as _ from 'lodash';
import { Injectable } from '#nestjs/common';
#Injectable()
export class TestService {
public test() {
return '1.0'; // and 1.1 in 1.1 directory
}
}
This is my module.ts:
import { Module, Logger } from '#nestjs/common';
import { TestModule as DorotTwo } from 'test-module/1.1/test.module';
import { TestModule as DorotOne } from 'test-module/1.0/test.module'
#Module({
controllers: [ProtobufController],
providers: [],
imports: [
DorotTwo,
DorotOne,
],
})
export class ProjectModule {
constructor() {
Logger.log('App initialized');
}
}
This is a simple test Controller in the project who want use the modules. A tried import TestService from 1.0 or 1.1 but the test function's response is always 1.0 because that is the last element in the import.
#Controller()
export class ProtobufController {
constructor(private readonly testService: TestService) {
console.log(this.testService.test()); // Always 1.0
}
.....
It is working if I use full different names for services for example (eg: UserAuthenticationService10, RegisterAuthenticationService10), but this is horrible and if i forget rename it in new version, it will overwrite.
Is exists an example where I can read how can I create this versioned module?
Would using custom providers be a satisfying solution for you?
Example:
// 1.0
#Module({
providers: [
{ provide: 'TestService_1.0', useClass: TestService }
]
})
export class TestModule {}
// 1.1
#Module({
providers: [
{ provide: 'TestService_1.1', useClass: TestService }
]
})
export class TestModule {}
// Then
#Controller()
export class ProtobufController {
constructor(
#Inject('TestService_1.0') private readonly testService_10,
#Inject('TestService_1.1') private readonly testService_11
) {
console.log(this.testService_10.test());
console.log(this.testService_11.test());
}
}
I obviously haven't tested this and you should adapt it to your usecase. I suggest you to have a look at https://docs.nestjs.com/fundamentals/custom-providers.

How to add forms to test in angular 5

I created an app in angular 5. Now I want to add some tests.
At the moment almost all the specs are failing even though I didnt even add anything.
For my first component it says:
Can't bind to 'formGroup' since it isn't a known property of 'form'.
How do i inject the necessary dependencys in the component?
Right now I have this:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
providers: [{
provide: Router,
useClass: class { navigate = jasmine.createSpy("navigate"); }
}, AuthentificationService, NotificationService, FormBuilder]
})
.compileComponents();
}));
This is the constructor of the component:
constructor(private router: Router, private auth: AuthentificationService, private ns: NotificationService, private fb: FormBuilder) {}
At the moment almost all the specs are failing even though I didnt even add anything.
Seems like you are using something like this or this.
For my first component it says: Can't bind to 'formGroup' since it isn't a known property of 'form'.
Something like this should take care of this particular error, note the imports-statement after declarations:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [ReactiveFormsModule],
providers: [{
provide: Router,
useClass: class { navigate = jasmine.createSpy("navigate"); }
}, AuthentificationService, NotificationService, FormBuilder]
})
.compileComponents();
}));
You'll of course have to add it to the imports at the file level as well but it seems you got those details already.

angular 5 Error: Can't resolve all parameters for Service

I could use a little help.
Context I am making a angular 5 module for an authentication service.
How do I pass a POJO into a class as parameters?
See my answer below.
I need to use HttpClient inside this auth service
I am getting this error:
Error: Can't resolve all parameters for AuthService: ([object Object], ?).
[object Object] is angular 5 HttpClient
Can some one please explain why I am getting this error and how to resolve it?
#NgModule()
export class AuthtModule {
static forRoot(params?: iParams) {
return {
ngModule: AuthModule,
provides: [
HttpClient,
{
provide: AuthService,
useFactory: setupAuthService,
deps: [ HttpClient, params ]
}
],
imports: [ HttpClientModule ],
exports: [ AuthService ]
}
}
}
Thanks in advance.
Cheers
I figured out how to do this, as it turns out the problem was with the second parameter (hence the question mark) not the first. the reason this error occurs is because in order for parameters to be passed into a class they must first be turned into a injectable.
Here is how you do it.
First create a class model with the params
foo-params.model.ts
export class FooParams {
public foo1: string;
public foo2: number;
}
Then In the Module class attributes set the class FooParams to use the values that are a POJO
app.module.ts
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { FooParams } from './foo-params';
#NgModule({
imports : [ HttpClientModule ],
providers: [{
HttpClient
{ provider: FooParams, useValue: params },
{
provider: BarService,
useFactory: setupBarService
deps: [ HttpClient, FooParams ]
}
}]
})
export class AppModule {}
And this is what the class that consumes the pojo would look like.
bar.service.ts
import { HttpClient } from '#angular/common/http';
import { FooParams } from './foo.service.ts';
expecto function setupBarService(http: HttpClient, params: FooParams) {
return new BarService(http, params);
}
#Injectable()
export class BarService {
constructor(http: HttpClient, params: FooParams) {}
//DO STUFF
}
Try by importing import 'core-js/es7/reflect'; in polyfills.ts

"No provider for AuthGuard!" using CanActivate in Angular 2

EDIT : Obviously this is outdated, now you provide your guard at the providers array in an NgModule. Watch other answers or official documentation for more information.
bootstrapping on a component is outdated
provideRouter() is outdated as well
I'm trying to setup Authentication in my project, using a login and AuthGuard from the Angular2 guide : https://angular.io/docs/ts/latest/guide/router.html
I'm using the release : "#angular/router": "3.0.0-beta.1".
I'll try to explain as much as possible, feel free to tell me if you need more details.
I have my main.ts file which boostraps the app with the following code :
bootstrap(MasterComponent, [
APP_ROUTER_PROVIDERS,
MenuService
])
.catch(err => console.error(err));
I load the MasterComponent, which loads a Header containing buttons that allow me to navigate through my app and it also contains my main for now.
I'm following the guide to make my app work the same way, with the following app.routes.ts :
export const routes: RouterConfig = [
...LoginRoutes,
...MasterRoutes
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes),
AUTH_PROVIDERS
];
And the login.routes.ts from the guide, which defines my AuthGuard :
export const LoginRoutes = [
{ path: 'login', component: LoginComponent }
];
export const AUTH_PROVIDERS = [AuthGuard, AuthService];
my Master component has its own route definition, which also contains the guard I'm trying to setup. master.routes.ts :
export const MasterRoutes : RouterConfig = [
{ path: '', redirectTo: '/accueil', pathMatch: 'full' },
{
path: 'accueil',
component: AccueilComponent
},
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
];
And I'm using the same files as the guide, which are auth.guard.ts, auth.service.ts, login.component.ts and login.routes.ts.
In my header.component.ts file, when I try to access any routes, it's working just fine, but when I try to access the guarded path (/dashboard), I get the No provider for AuthGuard! error.
I saw the recent post with the same issue as mine (NoProviderError using CanActivate in Angular 2), but to me the guard is bootstraped correctly up to the main.ts file, so my router should know which routes should be provided with the AuthGuard right ?
Any help or advice would be greatly appreciated.
Thanks !
I had this same issue after going through the Route Guards section of Routing and Authorization tutorial on the Angular website https://angular.io/docs/ts/latest/guide/router.html, it is section 5.
I am adding AuthGuard to one of my main routes and not to child routes like the tutorial shows.
I fixed it by added AuthGuard to my list of providers in my app.module.ts file, so that file now looks like this:
import { AppComponent } from './app.component';
import {AppRoutingModule} from './app-routing.module';
import {AuthGuard} from './auth-gaurd.service';
import { AnotherPageComponent } from './another-page/another-page.component';
import { LoginPageComponent } from './login-page/login-page.component';
#NgModule({
imports: [
BrowserModule,
FormsModule,
JsonpModule,
AppRoutingModule,
HttpModule
],
declarations: [
AppComponent,
LoginPageComponent,
AnotherPageComponent
],
providers: [AuthGuard],
bootstrap: [AppComponent]
})
export class AppModule { }
I have gone back through the tutorial and in their app.module.ts file, they do not add AuthGuard to the providers, not sure why.
Try to add
#Injectable({
providedIn: 'root'
})
no need to add to module provider.
Also, don't fall into the trap of using a literal for the guard class inside your routing configuration, just because some blog articles do:
{ path: 'whatever', component: WhatEverComponent, canActivate: ['WhatEverGuard'] }
is not going to work (No provider for...), instead, use the class directly:
{ path: 'whatever', component: WhatEverComponent, canActivate: [WhatEverGuard] }
Another hint, when lazy loading components, the guard is applied in the routing configuration of the parent component, not in the routing configuration of the lazy loaded component.
For those who still have this error - don't forget to include your AuthGuard service or class to main bootstrap function.
And don't forget to import this service before bootstrap runs.
import { bootstrap } from '#angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
import { AuthGuard } from './shared/auth.service';
bootstrap(AppComponent, [
appRouterProviders,
AuthGuard
]);
Angular 2 team did not mention this in main router docs, and it took couple of hours for me to figure it out.
The answer is further down in the tutorial. See the file listings in the "Add the LoginComponent" topic under the "Component-less route:..." section in "Milestone 5: Route Guards". It shows AuthGuard and AuthService being imported and added to the providers array in login-routing.module.ts, and then that module being imported into app.module.ts.
login-routing.module.ts
...
import { AuthGuard } from './auth-guard.service';
import { AuthService } from './auth.service';
...
#NgModule({
...
providers: [
AuthGuard,
AuthService
]
})
export class LoginRoutingModule {}
app.module.ts
import { LoginRoutingModule } from './login-routing.module';
#NgModule({
imports: [
...
LoginRoutingModule,
...
],
...
providers: [
DialogService
],
...
Actually, it was only a typo in an import...
I was typing
import { AuthGuard } from './../Authentification/auth.guard';
instead of
import { AuthGuard } from './../authentification/auth.guard';
making it not working but at the same time not displaying me any error...
(sadface)
I encountered this issue when I was following a tutorial. I tried most of the answer here but not getting any success. Then I tried the silly way like putting the AuthGuard before the other services in the provider and it works.
// app.module.ts
..
providers: [
AuthGuard,
UserService,
ProjectService
]
Since you got the solution as it was due to syntax issue. I just wanted to share this info.
we need to provide the AuthGaudSerivce as provider in only that module that correspond to respective route. No need to provide in main module or root module as main module will automatically load all the given sub module.This helps in keeping the code modular and encapsulated.
for example, suppose we have below scenario
1. we have module m1
2. we have route m1r in module m1
3. route m1r has 2 route r1 and r2
4. we want to protect r1 using authGaurd
5. finally we have main module that is dependent on sub module m1
Below is just prototype, not the actual code for understanding purpose
//m1.ts
import {AuthGaurd} from './auth.gaurd.service'
import {m1r} from './m1r'
#NgModule(
imports: [m1r],
providers: [AuthGaurd]
)
export class m1{
}
//m1r.ts
import {AuthGaurd} from './auth.gaurd.service'
const authRoute = [
{path: '/r1', component: 'authComponent', canActivate: [AuthGaurd]},
{path: '/r2', component: 'other'}
]
export authRoute
//main.module.ts
import {m1} from ''
import {mainComponent} from ''
#NgModule({
imports: [m1],
bootstrap: [mainComponent]
})
export class MainModule{}
import { Injectable } from '#angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (localStorage.getItem('currentUser')) {
// logged in so return true
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
return false;
}
}
Importing both HttpModule and HttpClientModule helped me.
import { HttpClientModule } from '#angular/common/http';
import { HttpModule } from '#angular/http';
you can try import AuthGuard in provider of that module and then import it in the routing component-routing.module.ts file also
#NgModule({
providers: [
AuthGuard
],})
This happened to me when I had setup my Routes incorrectly:
WRONG
const routes: Routes =
[
{
path: 'my-path',
component: MyComponent,
resolve: { myList: MyListResolver, canActivate: [ AuthenticationGuard ] }
},
];
Note that in this case canActivate was accidentally made a part of the resolve object.
CORRECT
const routes: Routes =
[
{
path: 'my-path',
component: MyComponent,
resolve: { myList: MyListResolver },
canActivate: [ AuthenticationGuard ]
},
];