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.
Related
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]
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],
})
Can't figure out what's the problem of my code. (I'm new with nestjs, I'm trying to learn it by passing some apps to it). Console log says:
Nest can't resolve dependencies of the UrlsAfipService (?). Please
make sure that the argument at index [0] is available in the ApiModule
context.
UrlsAfipService
import { Injectable } from '#nestjs/common';
import { AfipUrls } from './urls'
#Injectable()
export class UrlsAfipService {
constructor(
private readonly afipUrls: AfipUrls,
) {}
getWSAA () {
return this.afipUrls.homo().wsaa; // <- change to prod() for production
}
getService (service: string) {
return this.afipUrls.homo().service.replace('{service}', service)
}
}
AfipUrls
export class AfipUrls {
homo() {
return {
wsaa: 'https://url.com',
service: 'https://url.com'
}
}
prod() {
return {
wsaa: 'url.com',
service: 'url.com'
}
}
}
ApiModule
import { Module } from '#nestjs/common';
import { ApiController } from './api.controller';
import { UrlsAfipService } from './urls-afip.service'
import { WsaaService } from './wsaa.service'
import { DescribeService } from './describe.service';
#Module({
controllers: [ApiController],
providers: [UrlsAfipService, WsaaService, DescribeService]
})
export class ApiModule {}
AppModule
import { Module } from '#nestjs/common';
import { ApiModule } from './api/api.module';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [ApiModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
You have declared AfipUrls as a dependency for UrlsAfipService but it is not provided in any module.
So you have to add AfipUrls to the providers array of your ApiModule. Then it can be injected.
providers: [UrlsAfipService, WsaaService, DescribeService, AfipUrls]
// ^^^^^^^^
Note though, that encoding environment specific values in your code base might be a code smell. Consider creating a ConfigService that encapsulates environment specific variables that are read from environment variables or .env files using dotenv. See this answer for more information.
I've followed the example from the docs on how to create a basic config service.
At the bottom of the tutorial it says you can opt to declare it globally:
"Instead of importing ConfigModule repeatingly in all your modules, you can also declare ConfigModule as a global module."
So following the documentation for global modules I have:
Imported Global from #nestjs/common into ConfigModule.
Added the #Global() decorator to ConfigModule.
Imported ConfigModule into AppModule.
Added ConfigModule to the imports array.
So what's next? I have tried to inject ConfigService into AppService however it doesn't resolve.
app.module.ts:
import { Module } from '#nestjs/common';
import { AppService } from './app.service';
import { AppController } from './app.controller';
import { ConfigModule } from '../config/config.module';
#Module({
imports: [
ConfigModule,
],
controllers: [
AppController,
],
providers: [
AppService,
],
})
export class AppModule {}
app.service.ts:
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
private readonly config: ConfigService;
constructor(config: ConfigService) {
this.config = config;
}
getHello(): string {
return config.get('DB_NAME');
}
}
config.module.ts:
import { Module, Global } from '#nestjs/common';
import { ConfigService } from './config.service';
#Global()
#Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(`${process.env.NODE_ENV}.env`),
},
],
exports: [
ConfigService,
],
})
export class ConfigModule {}
config.service.ts:
import * as dotenv from 'dotenv';
import * as fs from 'fs';
export class ConfigService {
private readonly envConfig: { [key: string]: string };
constructor(filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath));
}
get(key: string): string {
return this.envConfig[key];
}
}
I expect to be able to inject ConfigService and access it from any module.
You're missing the this qualifier in your AppService:
getHello(): string {
return this.config.get('DB_NAME');
^^^^^
}
Also, the import is missing:
import { ConfigService } from './config/config.service';
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
) { ... }