I am trying to inject a service from one module to another module. I am keep on getting
Error: Nest can't resolve dependencies of the AwsSnsService (?). Please make sure that the argument function() {\n if (klass !== Object) {\n return klass.apply(this, arguments);\n }\n } at index [0] is available in the AwsModule context.
Below is the code
aws-sns.service.ts
#Injectable()
export class AwsSnsService {
constructor(private readonly awsSNS: AWS.SNS) {}
}
aws.module.ts
import { Module } from '#nestjs/common';
import { AwsSnsService } from './aws-sns.service';
#Module({
providers: [AwsSnsService],
exports: [AwsSnsService],
})
export class AwsModule {}
I want to use the AwsSnsService in my User module. I am doing it in a following way
#Module({
imports: [
SomeOtherModule,
AwsModule,
],
providers: [UserService, UserDevicePushTokensService],
exports: [UserService, UserDevicePushTokensService],
})
export class UserModule {}
#Injectable()
export class UserDevicePushTokensService {
constructor(private readonly awsSnsService: AwsSnsService) {}
}
Looks to me the dots are connected in the proper way. Still I am not figuring it out.
You must resolve dependency for AWS.SNS in AwsSnsService
Example with my s3, the same for SNS and any service:
s3.provider.ts
export const s3Providers: Provider = {
provide: S3_PROVIDER_KEY,
useFactory: async (): Promise<S3> => {
return new S3({
accessKeyId: "Key",
secretAccessKey: "AccessKey",
});
},
};
s3.service.ts
#Injectable()
export class S3Service {
constructor(
#Inject(S3_PROVIDER_KEY)
private readonly s3: S3) {}
}
You can read more here
Nestjs custom provider
Related
I have two modules which depend on each other.
GameModule.ts
import { Module, CacheModule, Global } from '#nestjs/common';
import { GameController } from './game.controller';
import { GameService } from './game.service';
import { PlayerModule } from '#src/player/player.module';
#Global()
#Module({
imports: [
CacheModule.register(),
PlayerModule,
],
controllers: [GameController],
providers: [GameService],
exports: [CacheModule, GameService],
})
export class GameModule {}
PlayerModule.ts
import { Module, Global } from '#nestjs/common';
import { PlayerController } from './player.controller';
import { PlayerService } from './player.service';
import { GameModule } from '#src/game/game.module';
import { Web3ManagerModule } from '#src/web3-manager/web3-manager.module';
#Global()
#Module({
imports: [GameModule, Web3ManagerModule],
controllers: [PlayerController],
providers: [PlayerService],
exports: [PlayerService],
})
export class PlayerModule {}
I defined in their services the other one with forwardRef
GameService.ts
...
constructor(
#Inject(CACHE_MANAGER) private cacheManager: Cache,
#Inject(forwardRef(() => PlayerService))
private playerService: PlayerService,
) {
...
PlayerService.ts
...
constructor(
#Inject(forwardRef(() => GameService))
private gameService: GameService,
private web3Manager: Web3ManagerService,
) {
...
but I keep getting the error:
Nest cannot create the GameModule instance.
The module at index [1] of the GameModule "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 [1] is of type "undefined". Check your import statements and the type of the module.
What am I missing?
I have a service for authentication based on JWT. To reuse this service in all my projects i created a library which should be shipped with npm.
For this service to work i need some API-Calls. In every project the API could look completely different so i don't want to provide this functionality inside my library instead inject another service which handles my API-Calls.
My idea was to create a module which contains my service and provide an interface to describe the service for API-Calls and inject it forRoot. The Problem is that my api service has some dependencies like HttpClient and i cannot simple instantiate it in my app.module.
My library looks like:
auth.module.ts
import { NgModule, ModuleWithProviders, InjectionToken } from '#angular/core';
import { AuthService } from '../services/auth.service';
import { AuthAPI } from '../models/authAPI';
import { AuthapiConfigService } from '../services/authapi-config.service';
#NgModule()
export class AuthModule {
static forRoot(apiService: AuthAPI): ModuleWithProviders {
return {
ngModule: AuthModule,
providers: [
AuthService,
{
provide: AuthapiConfigService,
useValue: apiService
}
]
};
}
}
auth-api.interface.ts
import { Observable } from 'rxjs';
export interface AuthAPI {
reqLogin(): Observable<{ access_token: string; }>;
reqRegister(): Observable<{ access_token: string; }>;
}
auth-api-config.service.ts
import { InjectionToken } from '#angular/core';
import { AuthAPI } from '../models/authAPI';
/**
* This is not a real service, but it looks like it from the outside.
* It's just an InjectionTToken used to import the config object, provided from the outside
*/
export const AuthapiConfigService = new InjectionToken<AuthAPI>('API-Service');
auth.service.ts
constructor(#Inject(AuthapiConfigService) private apiService) {}
How i am trying to implement it:
auth-rest-service.ts
import { Injectable } from '#angular/core';
import { AuthAPI } from 'projects/library-project/src/lib/auth/models/authAPI';
import { Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class AuthRestService implements AuthAPI {
constructor(private http: HttpClient) {}
reqLogin(): Observable<{ access_token: string; }> {
return this.http.post<{access_token: string}>(`/login`, 'test');
}
reqRegister(): Observable<{ access_token: string; }> {
return this.http.post<{access_token: string}>(`/login`, 'test');
}
}
app.module.ts
import { AuthRestService } from './components/auth-service/auth-rest.service';
#NgModule({
declarations: [
...
],
imports: [
...
AuthModule.forRoot(AuthRestService),
...
],
providers: [AuthModule],
bootstrap: [AppComponent]
})
export class AppModule { }
I can't create an instance of AuthRestService because of the dependencies this service has (HttpClient). Is there any method to tell angular to provide me this service.
This is possible with usage of angular's Injector.
import { Injector, ModuleWithProviders, NgModule, Optional, Provider, SkipSelf } from '#angular/core';
import { isFunction } from 'lodash';
export function resolveService(cfg: SharedConfig, inj: Injector): IncompleteService {
const provider = cfg?.service;
// if service is an angular provider, use Injector, otherwise return service instance as simple value
const service = isFunction(service) ? inj.get(provider) : provider;
return service;
}
/**
* Service to be implemented from outside the module.
*/
#Injectable()
export abstract class IncompleteService {
abstract strategyMethod();
}
// Optional: A config object is optional of course, but usually it fits the needs.
export interface SharedConfig {
service: IncompleteService | Type<IncompleteService> | InjectionToken<IncompleteService>;
// other config properties...
}
/*
* Optional: If a Config interface is used, one might resolve the config itself
* using other dependencies (e.g. load JSON via HTTPClient). Hence an InjectionToken
* is necessary.
*/
export const SHARED_CONFIG = new InjectionToken<SharedConfig>('shared-config');
// Optional: If SharedConfig is resolved with dependencies, it must be provided itself.
export type ModuleConfigProvider = ValueProvider | ClassProvider | ExistingProvider | FactoryProvider;
/**
* One can provide the config as is, i.e. "{ service: MyService }" or resolved by
* injection, i.e.
* { provide: SHARED_CONFIG: useFactory: myConfigFactory, deps: [DependentService1, DependentService2] }
*/
#NgModule({
declarations: [],
imports: []
})
export class SharedModule {
static forRoot(config: SharedConfig | ModuleConfigProvider): ModuleWithProviders<SharedModule> {
// dynamic (config is Provider) or simple (config is SharedConfig)
return {
ngModule: SharedModule,
providers: [
(config as ModuleConfigProvider).provide ? (config as Provider) : { provide: SHARED_CONFIG, useValue: config },
{ provide: IncompleteService, useFactory: resolveService, deps: [SHARED_CONFIG, Injector] },
// ... provide additional things
],
};
}
/**
* In general not really useful, because usually an instance of IncompleteService
* need other dependencies itself. Hence you cannot provide this instance without
* creating it properly. But for the sake of completeness, it should work as well.
*/
#NgModule({
declarations: [],
imports: []
})
export class MostSimpleSharedModule {
static forRoot(service: IncompleteService): ModuleWithProviders<SharedModule> {
// dynamic (config is Provider) or simple (config is SharedConfig)
return {
ngModule: SharedModule,
providers: [
{ provide: IncompleteService, useValue: service },
// ... provide additional things
],
};
}
EDIT
If you really need an interface iso. an (injectable) abstract class IncompleteService, you just need to define another InjectionToken<IncompleteServiceInterface> and provide this token explicitly.
I am trying to inject a provider to objects created by new. Is it possible? For example:
#Injectable()
export class SomeService {
}
export class SomeObject {
#Inject()
service: SomeService;
}
let obj = new SomeObject();
It's not working in my tests, the obj.service is undefined.
No, the dependency injection system will only work with objects that are created with it.
You can create SomeObject as a custom provider though, e.g. with useClass:
#Injectable()
class SomeObject {
constructor(private someService: SomeService) {}
}
#Module({
providers: [
SomeService,
{ provide: SomeObject, useClass: SomeObject },
],
})
export class ApplicationModule {}
or with useFactory for custom setup logic:
const someObjectFactory = {
provide: 'SomeObject',
useFactory: (someService: SomeService) => {
// custom setup logic
return new SomeObject(someService);
},
inject: [SomeService],
};
#Module({
providers: [SomeService, connectionFactory],
})
export class ApplicationModule {}
I have an injectable authentication service written for Angular 4. The code looks similar to the following:
auth.service.ts
import { CookieService } from 'ngx-cookie';
import { Identity } from './auth.identity';
export function authInit(authService: AuthService): () => Promise<any> {
return (): Promise<any> => authService.checkAuthenticated();
}
#Injectable()
export class AuthService {
identity: Identity;
isAuthenticated:boolean = false;
apiUrl: string = 'https://myUrl/api';
constructor(private _http: HttpClient, private _cookieService: CookieService) {
this.identity = new Identity();
}
checkAuthenticated(): Promise<any> {
return new Promise((res, rej) => {
let identity = this._cookieService.getObject('myToken');
if (!!identity) {
this.setAuthenticated(identity);
}
});
}
login(username: string, password: string) {
let creds = {
username: username,
password: password
};
this._http.post<any>(this.apiUrl + '/auth/login', creds).subscribe(data => {
this.setAuthenticated(data);
});
}
logout() {
}
private setAuthenticated(data: any) {
this._cookieService.putObject('myToken', data);
this.isAuthenticated = true;
// hydrate identity object
}
}
auth.module.ts
import { NgModule, APP_INITIALIZER } from '#angular/core';
import { CommonModule } from '#angular/common';
import { AuthService, authInit } from './auth.service';
#NgModule({
imports: [CommonModule],
providers: [
AuthService,
{
provide: APP_INITIALIZER,
useFactory: authInit,
deps: [AuthService],
multi: true
}
]
})
export class AuthModule { }
The idea is that when the app loads, I want to be able to check the local storage (cookies, sessionStorage or localStorage) to see if the value exists. (This is demonstrated by the commented if statement in the constructor.) Based on the isAuthenticated property I want to be able to show specific content.
Currently, if I uncomment the lines in the constructor, I'll get an exception document.* is not defined. I know what that means. Unfortunately, I don't know how to accomplish what I'm going for.
Keep in mind, this is a service and not a view component, so there's no ngOnInit method available.
EDITED
So I've added the factory provider as suggested. However, I'm still getting the exception: document is not defined
Thanks!
When you have a service that you need to have run before everything else might be initialized you can use the APP_INITIALIZER token (the documentation is sparse to say the least :)
The gist is that in your application providers array you add a factory provider:
{
provide: APP_INITIALIZER,
useFactory: authInit,
deps: [AuthService],
multi: true
}
Make sure to have provide set specifically to APP_INITIALIZER and the multi value to true. The authInit function is factory that returns a function that returns a promise. It has to return a promise and not an observable. It would be something like:
export function authInit(authServ: AuthService) {
return () => authServ.check();
}
The authServ.check() function is where you can put the logic you currently have commented in your service (just make sure it returns a promise as the result). Setting it up this way will let that logic run while the application loads.
Edit: Now that I take a look at the app.module.ts add the initialization of the cookie service and add the BrowserModule:
import { NgModule, APP_INITIALIZER } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { CommonModule } from '#angular/common';
import { CookieModule } from 'ngx-cookie';
import { AuthService, authInit } from './auth.service';
#NgModule({
imports: [BrowserModule, CommonModule, CookieModule.forRoot()],
providers: [
AuthService,
{
provide: APP_INITIALIZER,
useFactory: authInit,
deps: [AuthService],
multi: true
}
]
})
export class AuthModule { }
Also, make sure to add ngx-cookie to your systemjs.config.js (if that's what you're using as your loader).
Angular 2 - When injecting(importing) a service into my constructor I get a "No provider" error.
Here's my service I'm importing:
export class EnvironmentService {
public baseurl: string = "/baseurl/example/";
}
Here's my class with the error:
import {Http, Headers} from "angular2/http";
import {Injectable} from 'angular2/angular2';
import {EnvironmentService} from './environmentService';
#Injectable()
export class AuthService {
constructor(private _environmentService: EnvironmentService) {
//This is where the error is.
console.log(this._environmentService);
}
isUserLoggedIn = (): Promise<boolean> => {
return new Promise((resolve, reject) => {
resolve(true);
})
}
}
Add AuthService in app.module.ts Providers
Most likely you didn't specify EnvironmentService as a provider for your application. You can do it by passing all providers in a second argument of the bootstrap function, or using providers and viewProviders properties of the Component decorator:
bootstrap(AppComponent, [EnvironmentService /*, other providers */]);
// or
#Component({
// ... component config
providers: [EnvironmentService],
// or
viewProviders: [EnvironmentService]
})
class AppComponent { /* ... */ }
GO to the module that you want to use the EnvironmentService i.e app.module.ts or other modules.
after imports
you will see provider as an array
add the EnvironmentService
providers: [AuthService]