Nest.js Dependency Inversion function not found - dependency-injection

I followed the controller-service-repository architecture and I want to use dependency inversion on StoneRepository. Having the code from bellow I get:
[Nest] 22656 - 03/21/2022, 5:01:44 PM ERROR [ExceptionsHandler] this.stoneRepository.getStones is not a function
What have I done wrong?
Please help.
constants.ts
export const STONE_REPOSITORY_TOKEN = Symbol("STONE_REPOSITORY_TOKEN");
app.module.ts
import { Module } from "#nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { StoneModule } from "./stone/stone.module";
#Module({
imports: [StoneModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
stone.module.ts
import { Module } from "#nestjs/common";
import { StoneController } from "./stone.controller";
import { StoneService } from "./stone.service";
import { StoneRepository } from "./stone.repository";
import { STONE_REPOSITORY_TOKEN } from "./constants";
#Module({
imports: [],
controllers: [StoneController],
providers: [
{
provide: STONE_REPOSITORY_TOKEN,
useValue: StoneRepository,
},
StoneService,
],
})
export class StoneModule {}
stone.controller.ts
import { Controller, Get } from "#nestjs/common";
import { StoneService } from "./stone.service";
import { Stone } from "./domain/Stone";
#Controller()
export class StoneController {
constructor(private stoneService: StoneService) {}
#Get("/stone")
async getStones(): Promise<Stone[]> {
return await this.stoneService.getStones();
}
}
stone.interface.repository.ts
import { Stone } from "./domain/Stone";
export interface StoneInterfaceRepository {
getStones(): Promise<Stone[]>;
}
stone.service.ts
import { Inject, Injectable } from "#nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
import { STONE_REPOSITORY_TOKEN } from "./constants";
#Injectable()
export class StoneService {
constructor(
#Inject(STONE_REPOSITORY_TOKEN)
private stoneRepository: StoneInterfaceRepository,
) {}
async getStones(): Promise<Stone[]> {
return await this.stoneRepository.getStones();
}
}
stone.repository.ts
import { Injectable } from "#nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
#Injectable()
export class StoneRepository implements StoneInterfaceRepository {
async getStones(): Promise<Stone[]> {
return Promise.resolve([new Stone()]);
}
}

You are using useValue for the STONE_REPOSITORY_TOKEN token's custom provider. This means that Nest will inject the direct reference, not the class instance, so you have no access to instance methods, like getStones(). Change your module to this:
#Module({
imports: [],
controllers: [StoneController],
providers: [
{
provide: STONE_REPOSITORY_TOKEN,
useClass: StoneRepository,
},
StoneService,
],
})
export class StoneModule {}

Related

Can we export custom Guards from nestjs dynamic module and use it inside host module?

email-verification.guard.ts
#Injectable()
export class EmailVerificationGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const skipEmailVerification = this.reflector.get<boolean>('skipEmailVerification', context.getHandler());
if (skipEmailVerification) {
return true;
}
const request: Request = context.getArgs()[0];
if (!request.authPayload) {
throw new ForbiddenException('User not found');
}
if (!request.authPayload[Auth0Namespace.AppMetadata]) {
throw new ForbiddenException('Please verify your email before continuing');
}
return true;
}
}
dynamic-auth.module.ts
import { DynamicModule, Module } from '#nestjs/common';
import { authService } from './auth.service';
import { EmailVerificationGuard } from './email-verification.guard';
#Module({})
export class DynamicAuthModule {
static register(): DynamicModule {
return {
module: DynamicAuthModule,
providers: [authService, EmailVerificationGuard],
exports: [authService, EmailVerificationGuard],
};
}
}
app module (host)
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DynamicAuthModule, EmailVerificationGuard } from 'dyamic-auth-module';
#Module({
imports: [DynamicAuthModule.register({ folder: './config' })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Can I use EmailVerificationGuard in this host module? If not why?
Note:
nestjs packages version: 8.4.7
I tried this but I get this reflector dependency issue, How to resolve this?
Error: Nest can't resolve dependencies of the EmailVerificationGuard (?). Please make sure that the argument Reflector at index [0] is available in the AppModule context.
Potential solutions:
- If Reflector is a provider, is it part of the current AppModule?
- If Reflector is exported from a separate #Module, is that module imported within AppModule?
#Module({
imports: [ /* the Module containing Reflector */ ]
})
Is there any other way to handle this?

Property 'post' does not exist on type 'HttpClient' Angular 7

I am having the following error in an Angular 7 app:
Property 'post' does not exist on type 'HttpClient'
The code that I am using is in the following:
import { Injectable } from '#angular/core';
import { Car } from '../models/car';
import cars from '../cars/car-list';
import { HttpClient } from 'selenium-webdriver/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class CarService {
private cars: Car[];
constructor(private http:HttpClient) {
this.cars = cars;
}
addCar(car: Car): Observable<any> {
this.cars.push(car);
return this.http.post('http://localhost:3000/cars', car);
}
Can you help me sort out this error?!
Thanks in advance for your help.
Change:
import { HttpClient } from 'selenium-webdriver/http';
to:
import { HttpClient } from '#angular/common/http';
And also import HttpClientModule in components parent module

Angular 7 HTTP Interceptor is not working

I am new to Angular and I am trying to implement api call which send token in header on all api so I am using Interceptor.
I am not able to set header in request.
jwt.interceptor.ts
import { Injectable } from '#angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let currentUser = localStorage.getItem('useremail')
let currentUserToken = localStorage.getItem('token')
if (currentUser && currentUserToken) {
request = request.clone({
setHeaders: {
'Authorization': `${currentUserToken}`
}
});
}
// console.log("Request", request)
return next.handle(request);
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { AppUserListingComponent } from './app-user-listing/app-user-listing.component';
import { JwtInterceptor } from './jwt.interceptor';
#NgModule({
providers: [ { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true } ],
bootstrap: [AppComponent]
})
export class AppModule { }
user-listing.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserListingService {
apiUrl = "URL";
constructor(
private http: HttpClient
) { }
fetchAllUsers() {
return this.http.get(`${this.apiUrl}/fetchAdminsUsers`)
}
}
What is the reason my code is not working ?
Thanks in advance
In your app.module.ts code there's no import: [HttpClientModule] Do you have imported the HttpClientModule?
I'm experiencing a similar issue with angular 8.x http interceptor is not working OK.
Add imports: [BrowserModule, HttpClientModule] in #NgModule.
Authorization is a special header, you need to add withCredentials: true to the request to add it.
request = request.clone({
setHeaders: {
'Authorization': `${currentUserToken}`
},
withCredentials: true
});
try using instance of like below.
return next.handle(request).pipe(
tap(
event => {
if(event instanceof HttpResponse){
//api call success
console.log('success in calling API : ', event);
}
},
error => {
if(event instanceof HttpResponse){
//api call error
console.log('error in calling API : ', event);
}
}
)
)

Angular MatSnackBar not working from custom class

I am trying to create a toast using Material Snackbar from a custom class.
I am getting an error in my custom class ( Unable to find open from undefined. ) but working fine in user.service.ts
If ngZone is used, then i am getting an error, (unable to find run from undefined)
Note: In ErrorHandler Class
console.log(this.snackBar) // gives undefined
app.module.ts
providers: [ErrorHandler],
bootstrap: [AppComponent]
})
export class AppModule { }
User Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError(this.eh.handleError)
);
}
}
Error Handler Class
import {Component, Injectable, NgZone} from '#angular/core';
import { HttpErrorResponse } from '#angular/common/http';
import { throwError } from 'rxjs';
import * as _ from 'lodash';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class ErrorHandler {
constructor (private snackBar: MatSnackBar) {}
public handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error("Error code working")
console.log(this.snackBar) // gives undefined
this.snackBar.open("Hello world"); // Throwing Error
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
}
Thanks to all. I fixed it. Unfortunately i missed the stackoverflow link.
Change in
catchError((res) => this.eh.handleError(res))
did the trick
UserService
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError((res) => this.eh.handleError(res)) // change in this line
);
}
}
I stumbled upon the same issue in my application a few days ago. The reason is the context of this.
In
This.sb.open("hello world"); // Working Fine
the context of this is the UserService class. While in
this.snackBar.open("Hello world"); // Throwing Error
the context of this changed. Probably to CatchSubscriber.
You already mentioned that:
catchError((res) => this.eh.handleError(res)) // change in this line
resolves the error. Because it changed the context of this back to the UserService class. An alternative solution is to use the arrow syntax for the whole login():
login = (credentials) => {
// your code goes here
}

angular7 WARNING in Circular dependency detected:

I am trying to register a service toa specific module with the help of 'provideIn' attribute in my angular7 app.
test.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { TestComponent} from './test.component'
#NgModule({
declarations: [TestComponent],
imports: [
CommonModule
],
exports: [TestComponent]
})
export class TestModule { }
test.component
import { Component, OnInit } from '#angular/core';
import { TestServiceService } from '../test-service.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
constructor(public service: TestServiceService) { }
ngOnInit() {
}
}
test-service.ts
import { Injectable } from '#angular/core';
import { TestModule } from './test/test.module';
#Injectable({
providedIn: TestModule
})
export class TestServiceService {
val = 3;
constructor() { }
getDetails() {
return this.val;
}
}
when I run my app it shows below error
ERROR Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[TestComponent -> TestServiceService]:
StaticInjectorError(Platform: core)[TestComponent -> TestServiceService]:
NullInjectorError: No provider for TestServiceService!
when I change provideIn atrribute value as 'root' everything works fine.How can I register a service to specific module with the help of 'provideIn' attribute in angular7?
TestService has to be depend on TestModule in order to use providedIn to provide the service to the module. This creates a circular dependency, because TestModule has to depend on TestComponent which depends on TestService. Define the provider in the module so that the module depends on the component and the service.
#NgModule({
declarations: [TestComponent],
imports: [
CommonModule
],
providers: [
TestService
],
exports: [TestComponent]
})
export class TestModule { }
Define the service as Injectable, and set providedIn to null.
#Injectable({
providedIn: null
})
export class TestServiceService {
val = 3;
constructor() { }
getDetails() {
return this.val;
}
}
Change test.component to this :
import { Component, OnInit } from '#angular/core';
import { TestServiceService } from '../test-service.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
providers : [TestServiceService]
})
export class TestComponent implements OnInit {
constructor(public service: TestServiceService) { }
ngOnInit() {
}
}
and if you want to have single instance of service in module change test.module.ts to this :
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { TestComponent} from './test.component'
#NgModule({
declarations: [TestComponent],
imports: [
CommonModule
],
providers :[TestServiceService ],
exports: [TestComponent]
})
export class TestModule { }
do one of them

Resources