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.
Related
I am using angular 15.0 and installed #auth0/angular-jwt. the App.Module has configured as follows:
import {HttpClientModule} from "#angular/common/http";
import { JwtModule } from "#auth0/angular-jwt";
export function tokenGetter() {
return localStorage.getItem("jwt");
}
#NgModule({
declarations: [
...
],
imports: [
....
HttpClientModule,
...
JwtModule.forRoot({
config:{
tokenGetter: tokenGetter,
whitelistedDomains: ["localhost:5001"],
blacklistedRoutes: []
}
})
],
providers: [
....
],
exports: [
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(library: FaIconLibrary) {
library.addIconPacks(far, fab);
}
}
at first, when I imported the JWTModule, I got warnning that: Argument type {config: {tokenGetter: () => string | null, blacklistedRoutes: any[], whitelistedDomains: string[]}} is not assignable to parameter type Routes but no error!. so far, when I run the app, no pages is loaded and in the console this error is rised: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with EnvironmentInjector#runInContext.I found that this error is only for importing JwtModule and when I remove this, the main page of app is loaded and everything is normal. also, I created an AuthGuard service, I haven't used it yet, Because I assumed that the above error should be resolved first. the snippet code for service is:
import { Injectable } from '#angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "#angular/router";
import { JwtHelperService } from '#auth0/angular-jwt';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router:Router, private jwtHelper: JwtHelperService){}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const token = localStorage.getItem("jwt");
if (token && !this.jwtHelper.isTokenExpired(token)){
return true;
}
this.router.navigate(["login"]);
return false;
}
}
Thank you for your guidance on the correct use of the JwtModule of the Auth0/angular-jwt.
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.
There is the following standard NestJS code:
import { Controller, Get } from '#nestjs/common';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): string {
return this.appService.getHello();
}
}
Also there is the code for module app:
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
I don't understand how app understands that app controller expects to get app service as the first argument? At first I thought that app just looks at providers array and put all services from this array in controller's constructor. But I could change the code in this way:
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [],
controllers: [AppController],
providers: [AppService, NewService],
})
export class AppModule {}
Now I can use this NewService not only in AppController class, but in AppService class. How app knows that now I could use NewService in AppService class? Now I don't understand how we could do it, because TypeScript is just syntax sugar and exists only until compilation will be done and as I know app couldn't look at type of argument in AppService (for example fileService: FileService) and put the right service as argument when AppService will be created. I need technical details under the cover of this mechanism, not only use cases from official tutorial.
By registering it in the providers array you register it to the Nest IoC container.
If you check compiled js code, then you will find something like
WeatherHttpProvider = __decorate([
(0, common_1.Injectable)(),
__param(1, (0, common_1.Inject)(weather_parser_1.WEATHER_PARSER)),
__metadata("design:paramtypes", [axios_1.HttpService, Object])
], WeatherHttpProvider);
which in this case tells the WeatherHttpProvider about injected constructor params. This way it knows which class from the IoC container to use.
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 use this seed application to narrow down an error that keeps popping up (so debugging is easier...)
I keep getting this error when trying to add a data model to my shared module (in my browser console):
Error: (SystemJS) Can't resolve all parameters for Member: (?).(…)
The Member Class in question:
import * as _ from 'lodash';
import { Injectable } from '#angular/core';
#Injectable()
export class Member {
private id: string;
[key: string]: any;
constructor(private data?: any) {
if (data) {
this.id = data.id;
_.extend(this, data.attributes);
}
}
}
My SharedModule (the Member Class isn't referenced anywhere else for now):
import { NgModule, ModuleWithProviders } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { RouterModule } from '#angular/router';
import { ToolbarComponent } from './toolbar/index';
import { NavbarComponent } from './navbar/index';
import { NameListService } from './name-list/index';
import { Member } from './models/member.model';
#NgModule({
imports: [CommonModule, RouterModule],
declarations: [ToolbarComponent, NavbarComponent],
exports: [ToolbarComponent, NavbarComponent,
CommonModule, FormsModule, RouterModule]
})
export class SharedModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [NameListService, Member]
};
}
}
When I get rid of the constructor in class Member the error disappears:
import * as _ from 'lodash';
import { Injectable } from '#angular/core';
#Injectable()
export class Member {
private id: string;
[key: string]: any;
}
I am not using barrel imports as you can see since the order of the imports can cause the same error.
I am a bit stuck on how to solve this... Thanks
If the class is just to be used as a model, then don't add it to the #NgModule.providers and don't try to inject it. Just import the class into the class file where you need it, and just use it like you would any other normal class
import { Member } from './member.model';
#Component({})
class MyComponent {
member = new Member();
}
See Also:
Add Models/Entities/Objects to NgModule in Angular 2
Classes with the #Injectable() decorator get instantiated once by Angular as service providers. Angular uses reflection/type hinting to supply the instance with its dependency's.
Angular doesn't know what to give your Member class's constructor since its type is defined as any.