NestJS JwtModule.registerAsync() gives undefined at imports - javascript

when I nest start the code, it gives follow error, what is the problem?
I set ConfigModule as Gloabl, so I shouldn't need to import.
If I missed any code, please tell me, I can post it here.
I reference usage in NestJS Jwt package
I previously use JwtModule.register() with hardcoded secrets and options, it works fine.
[Nest] 159128 - 02/27/2021, 6:27:15 PM [ExceptionHandler] Nest cannot create the AuthModule instance.
The module at index [2] of the AuthModule "imports" array is undefined.
Potential causes:
- A circular dependency between modules. Use forwardRef() to avoid it. Read more: https://docs.nestjs.com/fundamentals/circular-dependency
- The module at index [2] is of type "undefined". Check your import statements and the type of the module.
TyprOrmModule access the database just fine, it proves configService picks up environment variables in .env
AppModule.ts:
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Connection } from 'typeorm';
import { join } from 'path';
import { UserModule } from './user/user.module';
import { AuthModule } from './auth/auth.module';
import { FormModule } from './form/form.module';
import { InventoryModule } from './inventory/inventory.module';
import { ConfigModule, ConfigService } from '#nestjs/config';
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true
//, ignoreEnvFile: true
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('DB_HOST'),
port: configService.get<number>('DB_PORT'),
username: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_DATABASE'),
synchronize: true,
autoLoadEntities: true,
}),
inject: [ConfigService],
})
, UserModule, AuthModule, FormModule, InventoryModule,
],
controllers: [AppController],
})
export class AppModule {
constructor(private connection: Connection) { }
}
if I add imports ConfigModule and inject ConfigService, it gives exact error at nest start. I just can't identify where the problem is.
AuthModule.ts:
import { Module } from '#nestjs/common';
import { AuthController } from './auth.controller';
import { LdapStrategy } from './ldap/ldap.strategy';
import { AuthService } from './auth.service';
import { JwtModule } from '#nestjs/jwt';
import { PassportModule } from '#nestjs/passport';
import { jwtConstants } from './jwt/constants';
import { JwtStrategy } from './jwt/jwt.strategy';
import { UserModule } from 'src/user/user.module';
import { JwtRefreshTokenStrategy } from './jwt/jwt.refresh.strategy';
import { ConfigModule, ConfigService } from '#nestjs/config';
#Module({
imports: [
PassportModule,
//ConfigModule,
JwtModule.registerAsync({
//imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>('JWT_SECRET'),
// signOptions: {
// algorithm: 'HS256',
// expiresIn: configService.get<number>('JWT_EXPIRES_IN_SEC'),
// }
}),
//inject: [ConfigService],
}),
, UserModule
],
controllers: [AuthController],
providers: [
LdapStrategy
, AuthService
, JwtStrategy
, JwtRefreshTokenStrategy
],
//exports: [AuthService],
})
export class AuthModule { }

Although this answer is pretty late, I ran into the exact same issue and found the solution on the Github site for #nestjs/jwt (https://github.com/nestjs/jwt#async-options). Posting here just in case anyone else runs into it.
It appears that you also need to inject the ConfigService as well.
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>('SECRET'),
}),
inject: [ConfigService],
})

Related

Everything is imported but Nest can't resolve dependencies of the stripePaymentService

I am integrating Stripe payment gateway through NestJS. I am getting the error which says "Nest can't resolve dependencies of the stripePaymentService". Although I am quite familiar with this error but somehow things are not working for me. I believe everything is imported where it should be.
This is complete error:
Error: Nest can't resolve dependencies of the stripePaymentService (?). Please make sure that the argument paymentIntentRepository at index [0] is available in the AppModule context.
Potential solutions:
- Is AppModule a valid NestJS module?
- If paymentIntentRepository is a provider, is it part of the current AppModule?
- If paymentIntentRepository is exported from a separate #Module, is that module imported within AppModule?
My stripePaymentModule is
/* eslint-disable prettier/prettier */
import { Module } from '#nestjs/common';
import { stripePaymentController } from './stripe-payment.controller';
import { stripePaymentService } from './stripe-payment.service';
import { TypeOrmModule } from '#nestjs/typeorm';
import { paymentIntent } from './entities/paymentIntent.entity';
import { ConfigModule } from '#nestjs/config';
#Module({
imports:[
ConfigModule.forRoot({
isGlobal: true,
}),
TypeOrmModule.forFeature([paymentIntent])],
controllers: [stripePaymentController],
providers: [stripePaymentService],
exports: [stripePaymentService],
})
export class stripePaymentModule {}
stripePaymentServices is
/* eslint-disable prettier/prettier */
import { Injectable, Logger } from '#nestjs/common';
import { InjectStripe } from 'nestjs-stripe';
import { ConfigService } from '#nestjs/config';
import Stripe from 'stripe';
import { paymentIntent } from './entities/paymentIntent.entity';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
#Injectable()
export class stripePaymentService {
stripe: Stripe;
logger = new Logger(stripePaymentService.name);
constructor(
#InjectRepository(paymentIntent)
private readonly paymentIntent:Repository<paymentIntent>,
)
{const stripeApiKey = process.env.STRIPE_SECRET_KEY;
this.stripe = new Stripe(stripeApiKey, {
apiVersion: '2022-11-15',
});}
async payment_intent(data: any, user) {
const intent = await this.stripe.paymentIntents.create({
amount: data.amount,
currency: data.currency,
description: data.description,
payment_method_types: ['card'],
});
return await this.paymentIntent.save({'userId':user.userId, 'intent_response':JSON.stringify(intent)})
// return intent;
}
}
And finally this is app.module
/* eslint-disable prettier/prettier */
import { Module } from '#nestjs/common';
import { UsersModule } from './users/users.module';
import { ConfigModule, ConfigService } from '#nestjs/config';
import { TypeOrmModule } from '#nestjs/typeorm';
import { AuthModule } from './auth/auth.module';
import { User } from './users/entities/user.entity';
import { PassportModule } from '#nestjs/passport';
import { MailModule } from './mail/mail.module';
import { FlightBookingModule } from './flight-booking/flight-booking.module';
import { TravelBookings } from './flight-booking/entities/bookingToken.entity';
import { stripePaymentService } from './stripe/stripe-payment.service';
import { stripePaymentController } from './stripe/stripe-payment.controller';
import { StripeModule } from 'nestjs-stripe';
import { stripePaymentModule } from './stripe/stripe-payment.module';
import { paymentIntent } from './stripe/entities/paymentIntent.entity';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
StripeModule.forRoot({
apiKey: process.env.STRIPE_API_KEY,
apiVersion: '2022-11-15'
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get('DB_HOST'),
port: +configService.get<number>('DB_PORT'),
username: configService.get('DB_USERNAME'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_NAME'),
entities: [User, TravelBookings, paymentIntent],
synchronize: true,
}),
inject: [ConfigService],
}),
PassportModule,
UsersModule,
AuthModule,
MailModule,
FlightBookingModule,
stripePaymentModule
],
controllers: [stripePaymentController],
providers: [stripePaymentService],
})
export class AppModule {}
The error is thrown from AppModule context. You wouldn't need to put stripePaymentService as a provider in AppModule.
If stripePaymentService is used by other modules, once you have export it, you just need to import the related module in a module that you want, and that would be enough.
So all you need to do is remove these lines from AppModule:
providers: [stripePaymentService]
You can remove this line as well:
controllers: [stripePaymentController]

APP_INITIALIZER not awaiting (Angular 13)

I am in the process of updating from okta/okta-angular 3.x to 5.x. It seems to have introduced an odd bug.
When the app first starts up, we have been using APP_INITIALIZER to execute appInitializerFactory(configService: ConfigService), which makes an http call to load configuration data.
The call looks like this:
public async loadConfig(): Promise<any> {
return this.httpClient.get('assets/config.json').pipe(settings => settings)
.toPromise()
.then(settings => {
this.config = settings as IAppConfig;
})
.catch(exception => {
console.log("Exception encountered while retreiving configuration");
});
}
Before updating to okta 5.x, the APP_INITIALIZER has been awaiting the promise. Now, it appears that other providers are being resolved before the promise in APP_INITILIZER has finished.
The resulting issue happens downstream at the oktaInitializerFactory, where it runs the following code:
public oktaConfig() {
return Object.assign({
onAuthRequired: (oktaAuth: OktaAuth, injector: Injector) => {
const router = injector.get(Router);
router.navigate(['/login']);
}
}, this.config.oktaConfig);
}
On the last line, this.config.oktaConfig is returning as undefined because the APP_INITIALIZER has not finished awaiting.
Heres the complete app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { Routes, RouterModule, Router } from '#angular/router';
import { APP_INITIALIZER, NgModule } from '#angular/core';
import { MaterialModule } from './modules/material/material.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { TablesComponent } from './components/tables/tables.component';
import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
import { ToolbarComponent } from './components/toolbar/toolbar.component';
import { LoginComponent } from './components/login/login.component';
import { FormsModule } from '#angular/forms';
import { RunLogComponent } from './components/run-log/run-log.component';
import { RunDataComponent } from './components/run-data/run-data.component';
import { ActionComponent } from './components/action/action.component';
import { ErrorsComponent } from './components/errors/errors.component';
import { OktaAuth } from '#okta/okta-auth-js';
import { OKTA_CONFIG, OktaAuthGuard, OktaAuthModule, OktaCallbackComponent } from '#okta/okta-angular';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { TabulatorUserTableComponent } from './components/tabulator-user-table/tabulator-user-table.component';
import { TabulatorTableComponent } from './components/tabulator-table/tabulator-table.component';
import { DataLakeComponent } from './components/data-lake/data-lake.component';
import { IntegrationHistoryComponent } from './components/integration-history/integration-history.component';
import { IntegrationStatusComponent } from './components/integration-status/integration-status.component';
import { MatSpinnerButtonComponent } from './components/mat-spinner-button/mat-spinner-button.component';
import { ConfigService } from './services/config.service';
import { DataStudioComponent } from './components/data-studio/data-studio.component';
import { IntegrationDashboardComponent } from './components/integration-dashboard/integration-dashboard.component';
import { IntegrationSelectorToolbarComponent } from './integration-selector-toolbar/integration-selector-toolbar.component';
import { PrimaryButtonsComponent } from './components/primary-buttons/primary-buttons.component';
import { UserCardComponent } from './components/user-card/user-card.component';
import { HttpOktaInterceptorService } from './services/http-okta-interceptor.service';
import { DebugInfoComponent } from './components/debug-info/debug-info.component';
import { ModalModule } from './modal';
import { DragNDrop } from './components/dropbox/drag-n-drop';
import { ProgressComponent } from './components/dropbox/progress/progress.component';
const appRoutes: Routes = [
{
path: '',
component: DashboardComponent,
canActivate: [OktaAuthGuard]
},
{
path: 'login/callback',
component: OktaCallbackComponent
},
{
path: 'login',
component: LoginComponent
}
];
#NgModule({
declarations: [
AppComponent,
TablesComponent,
PageNotFoundComponent,
DashboardComponent,
ToolbarComponent,
LoginComponent,
RunLogComponent,
RunDataComponent,
ActionComponent,
ErrorsComponent,
TabulatorUserTableComponent,
TabulatorTableComponent,
DataLakeComponent,
IntegrationHistoryComponent,
IntegrationStatusComponent,
MatSpinnerButtonComponent,
DataStudioComponent,
IntegrationDashboardComponent,
IntegrationSelectorToolbarComponent,
PrimaryButtonsComponent,
UserCardComponent,
DebugInfoComponent,
ProgressComponent,
DragNDrop
],
imports: [
BrowserModule,
OktaAuthModule,
RouterModule.forRoot(appRoutes, { relativeLinkResolution: 'legacy' }),
BrowserAnimationsModule,
MaterialModule,
FormsModule,
HttpClientModule,
ModalModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
deps:[ConfigService],
multi: true
},
{
provide: OKTA_CONFIG,
useFactory: oktaInitializerFactory,
deps:[ConfigService],
},
{
provide: HTTP_INTERCEPTORS,
useClass: HttpOktaInterceptorService,
multi: true,
deps: [OktaAuth]
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
export function appInitializerFactory(configService: ConfigService) {
return () => configService.loadConfig();
}
export function oktaInitializerFactory(configService: ConfigService) {
return configService.oktaConfig();
}
Is there a particular reason why my APP_INITIALIZER isn't finishing before other code executes? When I downgrade back to okta 3.x, this issue goes away.
As it turns out, upgrading from Okta 3 to 4+ or 5+ does introduce a change that makes loading auth server configuration in APP_INITIALIZER a non-viable option.
In a nutshell, okta-angular will attempt to connect to the auth server before the APP_INITIALIZER finishes. The workaround for this is to load the configuration data into the injector in main.ts, which fires off before the APP_INITIALIZER. This technically goes against the documentation in angular.io, but Okta support has verified this behavior [Source]
Another user does a similar implementation for Auth0, which encountered the same problem, linked here: stackoverflow.com/a/66957293/3202440
I have no idea how it was supposed to work in previous versions. you have appInitializerFactory that depends on ConfigService. ConfigService depends on HttpClient. HttpClient => HTTP_INTERCEPTORS i.e. on HttpOktaInterceptorService. HttpOktaInterceptorService => OktaAuth which most likely relies on OKTA_CONFIG.
It means that to construct a HttpClient(which is implicitly requried for initializer) you need OKTA_CONFIG already constructed. and it sounds very logical that this config initializer is being called earlier than APP_INITIALIZERs.
Most likely this dependency was introduced during refactoring, not just the update.
In your place I would try to eliminate dependency on a HttpClient in ConfigService and just make this request with a native api

Nest can't resolve dependencies of the service?

I'm trying access a database using a custom provider as per this guide. At startup, Nestjs throws the error Nest can't resolve dependencies of the EventsService (?). Please make sure that the argument DATA_SOURCE at index [0] is available in the AppModule context.
Here are my files
Database providers
import { DataSource } from 'typeorm';
export const databaseProviders = [
{
provide: 'DATA_SOURCE',
useFactory: async () => {
const dataSource = new DataSource({
type: "mysql",
host: "host",
port: 3306,
username: "username",
password: "password",
synchronize: true,
logging: true,
});
return dataSource.initialize();
},
},
];
Database module
import { databaseProviders } from "./database.providers";
import { Module } from "#nestjs/common";
#Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
Events service
import { Inject, Injectable } from '#nestjs/common';
import { DataSource } from 'typeorm';
import { DatabaseModule } from './database.module';
import { Event } from './entities/event.entity';
import { EventInvite } from './entities/eventInvite.entity';
#Injectable()
export class EventsService {
constructor(#Inject("DATA_SOURCE") private readonly database: DataSource) { }
createEvent(userId: string, event: Event) {
this.database.manager.create(Event, event)
}
deleteEvent(eventId: string){
this.database.manager.delete(Event, { eventId })
}
}
Events Module
import { Module } from '#nestjs/common';
import { DatabaseModule } from './database.module';
import { EventsController } from './events.controller';
import { EventsService } from './events.service';
#Module({
imports: [DatabaseModule],
controllers: [EventsController],
providers: [EventsService],
exports: [EventsService]
})
export class EventsModule {}
App module
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { EventsController } from './events/events.controller';
import { EventsService } from './events/events.service';
import { EventsModule } from './events/events.module';
import { DatabaseModule } from './events/database.module';
#Module({
imports: [],
controllers: [AppController, EventsController],
providers: [AppService, EventsService],
})
export class AppModule {}
If I import DatabaseModule inside of AppModule everything works. My question is, why is this required? My understanding thus far is that Nestjs builds a dependency tree, which in this case should look something like AppModule => EventService => DatabaseService. AppModule doesn't directly access DatabaseService, and therefore shouldn't need to import it directly, so why is Nestjs failing to resolve this dependency?
that module isn't global, thus its providers aren't globally available. As you're registering the service EventsService again in AppModule, you need to import the DatabaseModule
I believe this is what you're trying to do (which is pretty much what the docs shows):
#Module({
imports: [EventsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
than you won't have 2 instances of EventsService and EventsController anymore, only the one registered in EventsModule module.

Nest can't resolve dependencies of the NodeMailerService (?, AppConfigService)

I am facing this error although I have imported MailerModule in app.module and using it in other module named 'user.module'.
Attaching error Image and code
App.Module
import { NodeMailerService } from './mailer/mailer.service';
#Module({
imports: [
MailerModule.forRootAsync({
inject: [AppConfigService],
useFactory: (configService: AppConfigService) => {
const config = configService.smtpEmailObject;
return { ...config }
},
})
})
export class AppModule { }
Users.Module
import { NodeMailerService } from '../mailer/mailer.service';
#Module({
imports: [],
providers: [UsersService, ...userProviders, ...userMetaProviders,NodeMailerService],
exports: [UsersService],
controllers: [UsersController]
})
export class UsersModule { }
Error Image
Create MailerModule, add NodeMailerService to the providers and export NodeMailerService from it. Then import MailerModule in Users.Module and delete NodeMailerService from Users.Module.

How to inject $templateCache into angular hybrid APP_INITIALIZER hook?

I'm new to Angular5 and TypeScript, so it's very possible it's a simple thing I'm overlooking.
I have an Angular hybrid app that uses ngUpgrade to run AngularJS and Angular5 side-by-side. I'm trying to inject $templateCache into the OnAppInit function so that I can load all the AngularJS HTML templates before the app completely initializes. I'm getting the error "Cannot find name '$templateCacheService'" as indicated below. Is my syntax wrong or is this not possible?
I "upgrade" $templateCache in upgraded-providers.ts like this:
import { InjectionToken, Directive, ElementRef, Injector } from '#angular/core';
import { UpgradeComponent } from '#angular/upgrade/static';
export const $templateCacheService = new InjectionToken<any>('$templateCacheService');
export const $templateCacheServiceProvider = {
provide: $templateCacheService,
useFactory: (i: any) => i.get('$templateCache'),
deps: ['$injector']
};
Then in app.module.ts, I try to inject it into OnAppInit:
import { NgModule, APP_INITIALIZER, Inject } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { MatCommonModule } from '#angular/material';
import { FlexLayoutModule } from '#angular/flex-layout';
import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } from '#angular/common/http';
import { downgradeInjectable, UpgradeModule, downgradeComponent } from '#angular/upgrade/static';
import { environment } from '../environments/environment';
import {
$templateCacheServiceProvider,
$templateCacheService
} from './upgraded-providers';
import { AppComponent } from './app.component';
import { GlobalVarsService } from './core/global-vars.service';
import { WinAuthInterceptor } from './core/interceptors/win-auth.interceptor';
declare var angular: any;
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatCommonModule,
FlexLayoutModule,
HttpClientModule,
UpgradeModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: WinAuthInterceptor,
multi: true
},
{
provide: APP_INITIALIZER,
useFactory: OnAppInit,
multi: true,
deps: [GlobalVarsService, HttpClient, $templateCacheService]
},
GlobalVarsService,
$templateCacheServiceProvider
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule, private http: HttpClient) { }
ngDoBootstrap() {
angular.module('app').factory('globalVars', downgradeInjectable(GlobalVarsService));
this.upgrade.bootstrap(document.body, ['app'], { strictDi: true });
}
}
////// THIS NEXT LINE GETS error TS2304: Cannot find name '$templateCacheService' /////
export function OnAppInit(globalVars: GlobalVarsService, http: HttpClient, $templateCache: $templateCacheService) {
return (): Promise<any> => {
return new Promise((resolve, reject) => {
http.get(environment.apiBase + '/api/meta/data').subscribe(x => {
globalVars.MetaData = x;
globalVars.VersionNumber = globalVars.MetaData.versionNumber;
globalVars.IsDebugBuild = globalVars.MetaData.isDebugBuild;
globalVars.User = globalVars.MetaData.user;
globalVars.ApiBase = environment.apiBase;
globalVars.Templates.forEach(template => {
$templateCache.put(template.Item1, template.Item2);
});
resolve();
});
});
};
}
This is TypeScript type error, it doesn't affect how the application works (as long as compilation errors are ignored).
templateCacheService is not a valid type here, because $templateCacheService is a variable (injection token), not a type or an interface.
Only Angular class constructors are annotated with types for DI. Since factory functions are annotated with deps property, types in function signature exist only to provide type safety. If it's not needed, types can be skipped:
export function OnAppInit(
globalVars: GlobalVarsService, http: HttpClient,
$templateCache
) { ... }
Otherwise proper types should be used. $templateCache is an object with get, put, etc methods. Appropriate types are provided with AngularJS #types/angular type definitions. It will be something like:
export function OnAppInit(
globalVars: GlobalVarsService, http: HttpClient,
$templateCache: ng.ITemplateCacheService
) { ... }

Categories

Resources