How to set a parametr in the contructor of injection service? - dependency-injection

I have a service, in which I inject another service with parametr in constructor.
Main service
export class Test1Service {
constructor(
test2Service: Test2Service
) {}
getIndex() {
console.log(111);
}
}
Inject service
#Injectable()
export class Test2Service {
item;
constructor(name) {
if (name === 'blog') {
this.item = 'item1';
} else {
this.item = 'item2';
}
}
}

Fot this will change providers import in module:
#Module({
controllers: [AppController],
providers: [
Test1Service,
{
provide: 'BLOG',
useValue: new Test2Service('blog'),
},
{
provide: 'ANALYTICS',
useValue: new Test2Service('analytics'),
}
],
})
export class AppModule {}
Use it in service
#Injectable()
export class Test1Service {
constructor(
#Inject('BLOG') public testBlog: Test2Service,
#Inject('ANALYTICS') public testAnalytics: Test2Service
) {}
getIndex() {
this.testBlog.getIndex()
this.testAnalytics.getIndex()
}
}

Related

Can we export custom Guards from nestjs dynamic module and use it inside host module?

email-verification.guard.ts
#Injectable()
export class EmailVerificationGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const skipEmailVerification = this.reflector.get<boolean>('skipEmailVerification', context.getHandler());
if (skipEmailVerification) {
return true;
}
const request: Request = context.getArgs()[0];
if (!request.authPayload) {
throw new ForbiddenException('User not found');
}
if (!request.authPayload[Auth0Namespace.AppMetadata]) {
throw new ForbiddenException('Please verify your email before continuing');
}
return true;
}
}
dynamic-auth.module.ts
import { DynamicModule, Module } from '#nestjs/common';
import { authService } from './auth.service';
import { EmailVerificationGuard } from './email-verification.guard';
#Module({})
export class DynamicAuthModule {
static register(): DynamicModule {
return {
module: DynamicAuthModule,
providers: [authService, EmailVerificationGuard],
exports: [authService, EmailVerificationGuard],
};
}
}
app module (host)
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DynamicAuthModule, EmailVerificationGuard } from 'dyamic-auth-module';
#Module({
imports: [DynamicAuthModule.register({ folder: './config' })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Can I use EmailVerificationGuard in this host module? If not why?
Note:
nestjs packages version: 8.4.7
I tried this but I get this reflector dependency issue, How to resolve this?
Error: Nest can't resolve dependencies of the EmailVerificationGuard (?). Please make sure that the argument Reflector at index [0] is available in the AppModule context.
Potential solutions:
- If Reflector is a provider, is it part of the current AppModule?
- If Reflector is exported from a separate #Module, is that module imported within AppModule?
#Module({
imports: [ /* the Module containing Reflector */ ]
})
Is there any other way to handle this?

Nest.js Dependency Inversion function not found

I followed the controller-service-repository architecture and I want to use dependency inversion on StoneRepository. Having the code from bellow I get:
[Nest] 22656 - 03/21/2022, 5:01:44 PM ERROR [ExceptionsHandler] this.stoneRepository.getStones is not a function
What have I done wrong?
Please help.
constants.ts
export const STONE_REPOSITORY_TOKEN = Symbol("STONE_REPOSITORY_TOKEN");
app.module.ts
import { Module } from "#nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { StoneModule } from "./stone/stone.module";
#Module({
imports: [StoneModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
stone.module.ts
import { Module } from "#nestjs/common";
import { StoneController } from "./stone.controller";
import { StoneService } from "./stone.service";
import { StoneRepository } from "./stone.repository";
import { STONE_REPOSITORY_TOKEN } from "./constants";
#Module({
imports: [],
controllers: [StoneController],
providers: [
{
provide: STONE_REPOSITORY_TOKEN,
useValue: StoneRepository,
},
StoneService,
],
})
export class StoneModule {}
stone.controller.ts
import { Controller, Get } from "#nestjs/common";
import { StoneService } from "./stone.service";
import { Stone } from "./domain/Stone";
#Controller()
export class StoneController {
constructor(private stoneService: StoneService) {}
#Get("/stone")
async getStones(): Promise<Stone[]> {
return await this.stoneService.getStones();
}
}
stone.interface.repository.ts
import { Stone } from "./domain/Stone";
export interface StoneInterfaceRepository {
getStones(): Promise<Stone[]>;
}
stone.service.ts
import { Inject, Injectable } from "#nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
import { STONE_REPOSITORY_TOKEN } from "./constants";
#Injectable()
export class StoneService {
constructor(
#Inject(STONE_REPOSITORY_TOKEN)
private stoneRepository: StoneInterfaceRepository,
) {}
async getStones(): Promise<Stone[]> {
return await this.stoneRepository.getStones();
}
}
stone.repository.ts
import { Injectable } from "#nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
#Injectable()
export class StoneRepository implements StoneInterfaceRepository {
async getStones(): Promise<Stone[]> {
return Promise.resolve([new Stone()]);
}
}
You are using useValue for the STONE_REPOSITORY_TOKEN token's custom provider. This means that Nest will inject the direct reference, not the class instance, so you have no access to instance methods, like getStones(). Change your module to this:
#Module({
imports: [],
controllers: [StoneController],
providers: [
{
provide: STONE_REPOSITORY_TOKEN,
useClass: StoneRepository,
},
StoneService,
],
})
export class StoneModule {}

Implementing strategy in nest.js

I am trying to use the strategy pattern for the service, however the Module I try to use as context for strategy seems to only stick to one of the two. Here is the example code:
animal.module.ts
#Module({})
export class AnimalModule {
static register(strategy): DynamicModule {
return {
module: AnimalModule,
providers: [{ provide: 'STRATEGY', useValue: strategy }, AnimalService],
imports: [],
exports: [AnimalService]
};
}
}
animal.service.ts
#Injectable()
export class AnimalService {
constructor (#Inject('STRATEGY') private strategy) {
this.strategy = strategy
}
public makeSound() {
return this.strategy.makeSound()
}
}
cat.module.ts
#Module({
imports: [
AnimalModule.register(catStrategy),
],
controllers: [CatController],
providers: [CatService],
})
export class CatModule {}
cat.service.ts
#Injectable()
export class CatService {
constructor(
private readonly animalService: AnimalService,
) {}
public makeSound() {
return this.animalService.makeSound()
}
}
dog.module.ts
#Module({
imports: [
AnimalModule.register(dogStrategy),
],
controllers: [DogController],
providers: [DogService],
})
export class DogModule {}
dog.service.ts
#Injectable()
export class DogService {
constructor(
private readonly animalService: AnimalService,
) {}
public makeSound() {
return this.animalService.makeSound()
}
}
cat.strategy.ts
class CatStrategy {
public makeSound() {
return 'meow';
}
}
export const catStrategy = new CatStrategy();
Repo that replicates the issue: https://github.com/kunukmak/nestjs-strategy-problem-example
To clarify, both catService.makeSound and dogService.makeSound return "meow" in this case. Is it possible to make the dog bark?
I think you are looking for something like the following. Check the repo for the full example here. You can see below, we are registering a DynamicModule from the AnimalModule class:
#Module({
imports: [AnimalModule.register()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
The DynamicModule returned from the register() call is responsible for determining what implementation of the AnimalModule to provide. This means we can customize the AnimalModule based on configuration in the environment.
#Module({})
export class AnimalModule {
public static register(): DynamicModule {
const AnimalClassProvider = AnimalModule.getClassProvider();
return {
module: AnimalModule,
controllers: [AnimalController],
providers: [AnimalClassProvider],
exports: [AnimalClassProvider],
};
}
private static getClassProvider(): ClassProvider<AnimalService> {
const animalStrategy = process.env.ANIMAL_STRATEGY as AnimalStrategy;
const AnimalServiceClass = AnimalModule.getClassFromStrategy(animalStrategy);
return {
provide: AnimalService,
useClass: AnimalServiceClass,
};
}
private static getClassFromStrategy(strategy: AnimalStrategy): Type<AnimalService> {
switch (strategy) {
case AnimalStrategy.CAT: return CatService;
case AnimalStrategy.DOG: return DogService;
default: return AnimalService;
}
}
}
AnimalStrategy in this case is just an enum used to determine which implementation of the service we should provide.
With this approach, we allow Nest to construct the Provider along with all its dependencies properly. We are only responsible for telling Nest which implementation it will construct when it encounters an AnimalService dependency. This allows the rest of our application to be unaware of the implementation and only use our AnimalService abstraction.
From our AnimalController:
#Controller('animal')
export class AnimalController {
constructor(private readonly animalService: AnimalService) {}
#Post()
create(#Body() createAnimalDto: CreateAnimalDto) {
return this.animalService.create(createAnimalDto);
}
// ...
}
to another service in our application:
#Injectable()
export class PetOwnerService {
constructor(
private readonly animalService: AnimalService,
private readonly petOwnerService: PetOwnerService,
) {}
feedPet(petName: string) {
const petIsHungry = this.petOwnerService.isPetHungry(petName);
if (petIsHungry) this.animalService.feed(petName);
// ...
}
}

Can this kind of dependency injection be achieved with NestJS?

I want to inject the result of a Nest service method as dependency in a short way. Example is a logging facility, where a child logger is derived from the main logger with a new prefix.
It should be something like this (long version):
#Injectable()
class MyService {
private logger;
constructor(private loggerService: LoggerService) {
this.logger = loggerService.getChildLogger('prefix');
}
someMethod() {
this.logger.info('Hello');
}
}
But in a short version, something like this - maybe with a decorator:
#Injectable()
class MyService {
constructor(#logger('prefix') logger: LoggerService) {
}
someMethod() {
this.logger.info('Hello');
}
}
You could create providers for your loggers using factories injecting your LoggerService :
The LoggerService:
#Injectable()
export class LoggerService {
getChildLogger(scope: string) {
return new Logger(scope);
}
}
The MyService:
#Injectable()
export class MyService implements OnModuleInit {
constructor(#Inject('MY_SERVICE_LOGGER_TOKEN') public childLogger) {}
onModuleInit() {
this.childLogger.log('Hello World');
}
}
The module:
#Module({
providers: [
LoggerService,
MyService,
{
provide: 'MY_SERVICE_LOGGER_TOKEN',
useFactory: (loggerService) => loggerService.getChildLogger('scope'),
inject: [LoggerService]
},
]
})
export class MyModule {}
There is now a solution provided by Livio Brunner:
https://github.com/BrunnerLivio/nestjs-logger-decorator

Passing asp.net server parameters to Angular 2 app

================================================================
EDIT : SOLUTION After upgrading to 2.0 Final - Passing server parameters to ngModule after RC5 upgrade
==================================================================
Any way to have server parameters passed to an Angular 2 application?
i.e. I would like to use the MVC object "HttpContext.User.Identity.Name" and have it injectable anywhere in my angular 2 app.
In angular 1 this was possible using ng ".constant" and serializing .Net objects to JSON in index.cshtml.
Looks like there's a way to pass params but this doesn't work with .Net code.
Define global constants in Angular 2
//HTML - Bootstrapping
<script>
System.import('app/main').then(null, console.error.bind(console));
//I WOULD LIKE TO PASS SOME PARAMS TO APP/MAIN HERE
</script>
FINAL SOLUTION: (big thanks to Thierry)
index.cshtml:
<script>
System.import('app/main').then(
module =>
module.main(
{
name: '#User.Identity.Name',
isAuthenticated: User.Identity.IsAuthenticated.ToString().ToLowerInvariant(),
}
),
console.error.bind(console)
);
</script>
main.ts:
...
import {provide} from '#angular/core';
...
export function main(params) {
bootstrap(AppComponent,
[
provide('Name', { useValue: params.name }),
provide('IsAuthenticated', { useValue: params.isAuthenticated }),
ROUTER_PROVIDERS,
HTTP_PROVIDERS,
LoggerService,
AuthenticationService
]);
}
Usage:
import {Component, Injectable, Inject} from '#angular/core';
import {ROUTER_DIRECTIVES} from '#angular/router';
#Component({
selector: 'navbar',
templateUrl: 'app/components/header/navbar.html',
directives: [ROUTER_DIRECTIVES]
})
export class SomeComponent {
constructor(#Inject('Name') public username: string) {
}
}
An option would be to add a method in the module you import. So you can then call it to provide the object you want.
Here is a sample of the app/main module:
import {bootstrap} from '...';
import {provide} from '...';
import {AppComponent} from '...';
export function main(params) {
let userIdentityName = params.name; // for example
bootstrap(AppComponent, [
provide('userIdentityName', { useValue: userIdentityName })
]);
}
Then you can import it from your HTML main page like this:
<script>
System.import('app/main').then((module) => {
module.main({
userIdentityName: 'something from asp.net'
});
});
</script>
Update
With latest versions of Angular, you need to leverage modules this way:
export const USER_IDENTITY_NAME_TOKEN =
new InjectionToken('userIdentityName');
#NgModule({
(...)
providers: [
{
provide: USER_IDENTITY_NAME_TOKEN,
useValue: userIdentityName
}
]
})
export class MainModule() { }
thanks for info, for those using platformBrowserDynamic to boot:
main.ts:
//platformBrowserDynamic().bootstrapModule(asstModule);
export function main(appSettings: any) {
platformBrowserDynamic([{ provide: 'AppSettings', useValue: appSettings }]).bootstrapModule(asstModule);
}
With a .NET Core server, I recommend to use a the IOptions<> and a ViewComponent
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddOptions();
services.Configure<Models.EnvironmentSettings>(Configuration.GetSection("client"));
services.Configure<Models.EnvironmentSettings>(options =>
{
options.OtherSetting = "Other";
});
services.AddMvc();
}
Models/EnvironmentSettings.cs
public class EnvironmentSettings
{
public string OtherSetting { get; set; }
public string LoginUrl { get; set; }
}
appsettings.json
{
"client": {
"LoginUrl": "http://localhost:45290/Token"
}
}
Controllers/Components/BootstrapViewComponent.cs
public class BootstrapViewComponent : ViewComponent
{
private IOptions<EnvironmentSettings> environmentSettings;
public BootstrapViewComponent(
IOptions<EnvironmentSettings> environmentSettings
)
{
this.environmentSettings = environmentSettings;
}
public async Task<IViewComponentResult> InvokeAsync()
{
return View(environmentSettings.Value);
}
}
Views/Shared/Components/Bootstrap/Default.cshtml
#model YourApp.Models.EnvironmentSettings
<script>
System.import('app')
.then(function (module) {
module.main({
other: "#Model.OtherSetting",
loginUrl: "#Model.LoginUrl"
})
})
.catch(function (err) {
console.error(err);
});
</script>
Views/Shared/_Layout.cshtml
<head>
...
#await Component.InvokeAsync("Bootstrap")
</head>
main.ts
export function main(settings: any) {
platformBrowserDynamic([{ provide: 'EnvironmentSettings', useValue: settings }]).bootstrapModule(AppModule);
}

Resources