I'm trying to do what I think it's a stupid simple endpoint and some how it seems nestjs is not resolving the observable from Axios which results in the request hanging until it reaches the timeout.
I've stripped down my entire application to barebones, literally, in the pursuit of the issue and I still can't really find anything....
main.ts:
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(8080);
}
bootstrap();
app.module.ts:
import { Module } from '#nestjs/common';
import { AuthModule } from './auth/authModule';
#Module({
imports: [AuthModule],
})
export class AppModule {}
AuthModule.ts:
import { Module } from '#nestjs/common';
import { AuthController } from './controllers/auth.controller';
import { HttpModule } from '#nestjs/axios';
#Module({
controllers: [AuthController],
imports: [HttpModule],
})
export class AuthModule {}
Auth.controller.ts:
import { Controller, Post } from '#nestjs/common';
import { HttpService } from '#nestjs/axios';
import { Observable } from 'rxjs';
import { AxiosResponse } from 'axios';
#Controller('auth')
export class AuthController {
constructor(private httpService: HttpService) {}
#Post('/login')
login(): Observable<AxiosResponse<string>> {
console.log('hit');
return this.httpService.get<string>('http://auth-ms/health');
}
}
package.json:
"dependencies": {
"#liaoliaots/nestjs-redis": "^8.2.1",
"#metamask/eth-sig-util": "^4.0.0",
"#nestjs/axios": "^0.0.8",
"#nestjs/common": "^8.0.0",
"#nestjs/config": "^2.0.0",
"#nestjs/core": "^8.0.0",
"#nestjs/platform-express": "^8.0.0",
"#nestjs/typeorm": "^8.1.4",
"axios": "^0.27.2",
"faker": "^6.6.6",
"ioredis": "^5.0.3 || ^0.3.6",
"jsonwebtoken": "^8.5.1",
"jwt-decode": "^3.1.2",
"nestjs-ethers": "^1.0.1",
"nestjs-real-ip": "^2.1.0",
"pg": "^8.7.3",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"typeorm": "^0.3.6",
},
Any clue on what's going on here? I guess I'm missing something small and stupid but... can't really figure out what.
So, AxiosResponse is a complex data structure and contains circular dependencies.
So you need to map it because you can't return it like this (except if you use a lib that parse circular JSON)
So you need to map the observable to only get the data of the website.
#Post('/login')
login(): Observable<string> {
console.log('hit');
return this.httpService
.get<string>('http://auth-ms/health')
.pipe(map((v) => v.data));
}
The Observable class works like a Promise and it handles I/O commands asynchronously by default. To return the result from a gateway request, like you are trying to do, you'll need to resolve the Observable synchronously.
To do it, you need to use the firstValueFrom or lastValueFrom functions from rxjs lib to convert it to a Promise and await the response:
import { Controller, Post } from '#nestjs/common';
import { HttpService } from '#nestjs/axios';
import { Observable, firstValueFrom } from 'rxjs';
import { AxiosResponse } from 'axios';
#Controller('auth')
export class AuthController {
constructor(private httpService: HttpService) {}
#Post('/login')
async login(): Promise<string> {
const responseObserver = this.httpService.get<string>('http://auth-ms/health');
const response = await firstValueFrom(responseObserver);
return response.data;
}
}
Related
I'm trying to use a jwt.strategy.ts to protect my endpoints with jwt verification. Everything was going ok until I decided to import a custom JWTService inside this jwt.strategy which contains its own JWTModule but Nest doesn't seems to recognize it. I can use the JWTService in other services but it doesn't work inside the strategy. What should I do ? What am I doing wrong ?
The NEST Message:
[Nest] 53 - 09/22/2022, 6:32:25 PM ERROR [ExceptionHandler] Nest can't resolve dependencies of the JwtStrategy (ConfigService, ?). Please make sure that the argument Object at index [1] is available in the AuthModule context.
Potential solutions:
- If Object is a provider, is it part of the current AuthModule?
- If Object is exported from a separate #Module, is that module imported within AuthModule?
#Module({
imports: [ /* the Module containing Object */ ]
})
Error: Nest can't resolve dependencies of the JwtStrategy (ConfigService, ?). Please make sure that the argument Object at index [1] is available in the AuthModule context.
Potential solutions:
- If Object is a provider, is it part of the current AuthModule?
- If Object is exported from a separate #Module, is that module imported within AuthModule?
#Module({
imports: [ /* the Module containing Object */ ]
})
The JWTModule:
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { jwtOptions } from './jwt.config';
import { JWTService } from './jwt.service';
#Module({
imports: [JwtModule.registerAsync(jwtOptions)],
providers: [JWTService],
exports: [JWTService],
})
export class JWTModule {}
The JWTService:
import { Request } from 'express';
import { DecodeOptions } from 'jsonwebtoken';
import { Injectable, UnprocessableEntityException } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { JwtService, JwtSignOptions, JwtVerifyOptions } from '#nestjs/jwt';
import { CookieHttpConfig } from '../auth';
#Injectable()
export class JWTService {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
sign(payload: string | object | Buffer, options?: JwtSignOptions) {
return this.jwtService.sign(payload, options);
}
async signAsync(payload: string | object | Buffer, options?: JwtSignOptions) {
return this.jwtService.signAsync(payload, options);
}
verify(token: string, options?: JwtVerifyOptions) {
return this.jwtService.verify(token, options);
}
async verifyAsync(token: string, options?: JwtVerifyOptions) {
return this.jwtService.verifyAsync(token, options);
}
decode(token: string, options?: DecodeOptions) {
return this.jwtService.decode(token, options);
}
async getToken(tokenPayload: any): Promise<string> {
try {
const token: string = await this.jwtService.signAsync(tokenPayload);
return `Bearer ${token}`;
} catch (error) {
throw new UnprocessableEntityException(error.message);
}
}
async refreshToken(
cookieName: string,
request: Request,
payload: any,
): Promise<void> {
const token: string = await this.getToken(payload);
request.res.cookie(cookieName, token, CookieHttpConfig.Options());
}
}
jwt.strategy:
import { JWTService } from '#app/common';
import { Injectable } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { PassportStrategy } from '#nestjs/passport';
import { Request } from 'express';
import { ExtractJwt, Strategy } from 'passport-jwt';
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly configService: ConfigService,
private readonly jwtService: JWTService,
) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
try {
const token = request.signedCookies['Authorization'].split(' ')[1];
return token;
} catch (error) {
return null;
}
},
]),
ignoreExpiration: false,
secretOrKey: configService.get('AUTH_JWT_SECRET'),
});
}
async validate(request: Request, payload: any): Promise<any> {
const tokenPayload = {
email: payload.email,
id: payload.id,
};
await this.jwtService.refreshToken('Authorization', request, tokenPayload);
return tokenPayload;
}
}
Extra information about my project:
I divided the project into a monorepo, so I imported the JWTModule inside the AuthModule but it still doesn't work. The jwt.strategy.ts and the JWTModule is inside a shared lib created at the same level as the apps folders containing the microservices.
The AuthModule:
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { UsersModule } from '../users/users.module';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import {
AuthLibModule,
JWTModule,
JwtStrategy,
LocalStrategy,
} from '#app/common';
import { Web3Module } from '../web3/web3.module';
import { VonageModule } from '#app/common';
#Module({
imports: [
UsersModule,
PassportModule,
JWTModule,
Web3Module,
VonageModule,
AuthLibModule,
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy],
})
export class AuthModule {}
Im having some issues with validation a jwt in nestjs. Alot of documentation and tutorials show how to create token etc. I just want to validate it and protect graphql resolvers.
In my jwt.strategy.ts i have this code:
import { Injectable } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { PassportStrategy } from '#nestjs/passport';
import { passportJwtSecret } from 'jwks-rsa';
import { ExtractJwt, Strategy } from 'passport-jwt';
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'xxxxx',
}),
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
audience: 'urn:microsoft:userinfo',
issuer: 'xxxxx',
algorithms: ['RS256'],
});
}
validate(payload: unknown): unknown {
return payload;
}
}
And in my authz module i have:
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
#Module({
imports: [PassportModule.register({ defaultStrategy: 'jwt' })],
providers: [JwtStrategy],
exports: [PassportModule],
})
export class AuthzModule {}
In my resolvers i use the UseGuards decorator:
#UseGuards(AuthGuard('jwt'))
All this should work, but i get an error:
TypeError: Cannot read properties of undefined (reading 'logIn')
I dont want to do any login, just validate the token.
ANy tips on this? Have tried google, been at it for hours. Thanks!
I've used Mongodb native node driver for my Nestjs project and when I run nest run command I faced this error:
Nest can't resolve dependencies of the ProjectService (?). Please make
sure that the argument DATABASE_CONNECTION at index [0] is available
in the AppModule context.
Potential solutions:
If DATABASE_CONNECTION is a provider, is it part of the current AppModule?
If DATABASE_CONNECTION is exported from a separate #Module, is that module imported within AppModule? #Module({
imports: [ /* the Module containing DATABASE_CONNECTION */ ] })
The provider for DATABASE_CONNECTION has been defined in the database module and database module has been imported in the appModule and I can't find out the problem.
src/app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { ProjectController } from './project/project.controller';
import { ProjectService } from './project/project.service';
import { DatabaseModule } from './database.module';
#Module({
imports: [DatabaseModule],
controllers: [AppController, ProjectController],
providers: [ProjectService],
})
export class AppModule {}
src/database.module.ts
import { Module } from '#nestjs/common';
import { MongoClient, Db } from 'mongodb';
#Module({
providers: [{
provide: 'DATABASE_CONNECTION',
useFactory: async (): Promise<Db> => {
try {
const client = await MongoClient.connect('mongodb://127.0.0.1:27017', {
useUnifiedTopology: true
});
return client.db('app-test');
} catch(e){
throw e;
}
}
}
],
exports:[
'DATABASE_CONNECTION'
]
})
export class DatabaseModule { }
src/project/project.service.ts
import { Inject, Injectable } from '#nestjs/common';
import { Db } from 'mongodb';
import { Project } from '../models/project.model';
#Injectable()
export class ProjectService {
constructor(
#Inject('DATABASE_CONNECTION')
private db: Db
) {
}
async getProjects(): Promise<Project[]> {
return this.db.collection('Projects').find().toArray();
}
}
I finally fixed the error. I removed the content of dist folder and built the project again and start it and error fixed!
I think this could be helpful https://stackoverflow.com/a/66771530/3141993 for avoiding these type of errors without removing the content of dist file manually.
I'm setting up a Ionic 4 project using ngx-translate and a custom loader to load JSON translations from an external domain. I've been following this guys take on it: https://forum.ionicframework.com/t/ngx-translate-translatehttploader-with-external-url/99331/4
Stackblitz link: https://stackblitz.com/edit/ionic-v4-jdfbh6
So this is my custom loader (provider).
#Injectable()
export class TranslationProvider implements TranslateLoader {
constructor(private http: HttpClient) {
console.log('Hello TranslationProvider Provider');
}
getTranslation(lang: string): Observable<any> {
return Observable.create(observer => {
this.http.get<any>(Environment.base_api + '/static/translations/' + lang + 'json', {
headers: {'Content-Type': 'application/json'}}).subscribe((res: Response) => {
observer.next(res.json());
observer.complete();
});
});
}
}
and in my app.module.ts (imports):
imports: [
BrowserModule,
IonicModule.forRoot(App),
IonicStorageModule.forRoot(),
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (TranslationProvider),
deps: [HttpClient]
}
})
],
The error message I receive is:
TypeError: Cannot set property 'http' of undefined at TranslationProvider (http://localhost:8100/build/main.js:1073:19)
I made a working sample app, here's the gist:
https://gist.github.com/olivercodes/a34be66e5b69edcd96038e5a4518b16e
You need to change #Injectable() to
#Injectable({
providedIn: 'root'
})
Also, make sure these are your import locations:
// In the service file
import { HttpClient } from '#angular/common/http';
import { TranslateLoader } from '#ngx-translate/core'
// in app.module
import { TranslateLoader } from '#ngx-translate/core'
import { HttpClient, HttpClientModule } from '#angular/common/http';
Use my provided gist and make sure your imports are right.
I am calling REST API from ionic 3 and Http Client, I am using Http Interceptor, When I am setting header name in the code, it is going under "Access-Control-Request-Headers:" see the attached Screenshot
and my code is :
import { Injectable, NgModule } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '#angular/common/http';
import { HTTP_INTERCEPTORS } from '#angular/common/http';
#Injectable()
export class HttpsRequestInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
const dupReq = req.clone({ headers: req.headers.set('Access-Control-Allow-Origin', '*').append('ABC','xxx') });
return next.handle(dupReq);
}
};
#NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpsRequestInterceptor, multi: true }
]
})
export class InterceptorModule { }
You have handled cors on the frontend, But it also needs to be handled from the backend, Moreover, install this extension and turn it on before making the http call and you will be able to receive response
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en