NestJS - avoid setContext() in constructor on logger injection - javascript

In NestJS I have custom logger:
import { Injectable, Logger, Scope } from '#nestjs/common';
#Injectable({ scope: Scope.TRANSIENT })
export class LoggerService extends Logger {
log(message: any) {
super.log(message);
}
error(message: any) {
super.log(message);
}
warn(message: any) {
super.log(message);
}
debug(message: any) {
super.log(message);
}
verbose(message: any) {
super.log(message);
}
setContext(context: string) {
super.context = context;
}
}
It is registered globally:
import { Global, Module } from '#nestjs/common';
import { LoggerService } from './logger.service';
#Global()
#Module({
providers: [LoggerService],
exports: [LoggerService],
})
export class LoggerModule {}
Is there any way to somehow pass context on injection in service constructor and avoid execution of logger.setContext(context) in every service - instead just set it in LoggerService constructor?
Example usage now:
constructor(private logger: LoggerService) {
this.logger.setContext(ClassName.name);
}
Expected usage:
constructor(private logger: LoggerService) {}

When you do private logger: LoggerService, there's no chance to make that .setContext(Service.name) call.
What you could do is something like:
#Logger(Service.name) private logger: LoggerService
How? Read this article: Advanced NestJS: Dynamic Providers

Related

NestJs: How can I inject a Provider that has a constructor with a Mongoose Model?

Hi I have the following code in NestJS.I am using moduleRef and have declared a custom provider named 'blogService'.However I am getting an error that says:
'Nest can't resolve dependencies of the blogService (?). Please make
sure that the argument BlogModel at index [0] is available in the
AppModule context.'
.What exactly am I doing wrong while declaring the custom provider which is leading to this error as it seems that I am injecting the Mongoose Model as well?
app.module.ts
import { BlogService } from './blog/service/blog.service';
import { Blog } from './blog/schema/blog.schema';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '#nestjs/config';
import { MongooseModule, getModelToken } from '#nestjs/mongoose';
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, load: [configuration], }),
MongooseModule.forRoot(process.env.DATABASE_URL)
],
controllers: [AppController],
providers: [AppService, {
provide: 'blogService',
useFactory: () => BlogService,
inject: [getModelToken(Blog.name)]
}],
})
app.controller.ts
import { Controller, Get } from '#nestjs/common';
import { ModuleRef } from '#nestjs/core';
import { BlogService } from './blog/service/blog.service';
#Controller()
export class AppController {
constructor(private readonly modelRef: ModuleRef) { }
#Get('blogs')
async getAllBlogs(): Promise<any> {
const response = await this.modelRef.get('blogService', { strict: false }).getAllBlogs();
return response;
}
}
blog.service.ts
import { Injectable, HttpException, HttpStatus, Logger } from '#nestjs/common';
import { Blog } from '../schema/blog.schema';
import { InjectModel } from '#nestjs/mongoose';
import { Model, Types } from 'mongoose';
#Injectable()
export class BlogService {
private readonly logger = new Logger(BlogService.name);
constructor(#InjectModel(Blog.name) private blogModel: Model<Blog>) { }
async getAllBlogs() {
try {
const blogs = await this.blogModel.find().exec();
return blogs;
} catch (error) {
this.logger.error(error.message);
}
}
}
Not really sure why you're making a custom provider for this, other than possibly academic purposes. Either way, for anything you want to #InjectModel() or use getModelToken() for, you needx to have a MongooseModule.forFeature() call to register the custom provider the #nestjs/mongoose package will create for you. Once you have this you can use #InjectModel() or a custom provider like
{
provide: 'blogService',
inject: [getModelToken(Blog.name)],
useFactory: (model: Model<Blog>) => new BlogService(model)
}

Error: Nest can't resolve dependencies of the AwsSnsService (?)

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

How to send an ajax request in the angular 6?

I am completely unfamiliar with the angular since I am a back-end developer. To test my api, I need to send an ajax request from angular.
Tell me how to do this?
There is a code. The request must be executed before clearing the localeStorage.
<button (click)="logoutAndClose()" class="btn_green btn_ml" mat-raised-button>
Log out
</button>
#Component({
selector: 'app-logout-modal',
templateUrl: './logout-modal.component.html',
styleUrls: ['./logout-modal.component.scss']
})
export class LogoutModalComponent implements OnInit {
constructor(public thisDialogRef: MatDialogRef<LogoutModalComponent>,
private router: Router,
private http: HttpClient,
#Inject(MAT_DIALOG_DATA) public data: any) {
}
ngOnInit() {
}
logoutAndClose(): void {
this.http.post("http://127.0.0.1:8001/api/v1/users/settings/logout/")
localStorage.clear();
this.thisDialogRef.close();
this.router.navigateByUrl(RouteUrls.Login);
}
}
As a best practice you should create a service to send HTTP requests:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class YourService {
private url: string = "http://api";
private endpoint:string = "car";
constructor(private http: HttpClient,
) { }
get(id: number): Observable<Car> {
return this.httpClient
.get<Car>(`${this.url}/${this.endpoint}/${id}`)
.pipe(map(data => data));
}
}
and then you will be available to use built in dependency injection in your component:
export class YourCarComponent {
constructor(private yourService: YourService) {
}
getCars(id: number) {
this.yourService.get(id)
.subscribe(s=> console.log(s));
}
UPDATE:
In order to execute your http query, you need to run it. So you need to call subscribe method:
this.http.post("http://127.0.0.1:8001/api/v1/users/settings/logout/")
.subscribe(s => console.log(s));
In addition, as a best practice should not contain an implementation details of http requests because it is not deal of view. View should just show data.
You need to import the HTTPModule
#NgModule({
imports: [
BrowserModule,
// import HttpClientModule after BrowserModule.
HttpClientModule,
],
Inject inside constructor:
#Injectable()
export class YourService {
constructor(private http: HttpClient) { }
}
this.http.get(this.url).subscribe((data: CanBeDirectlyMapToJsonObject) => {
});
For More details refer to https://angular.io/guide/http

Can't resolve all parameters in validation directive during ng build --prod

I have go through with following questions but didn't found any solution.
Angular 4 directive error: Can't resolve all parameters for directive
Can't resolve all parameters for custom directive
I have made a custom validation directive to validate unique permalink. this code working fine but when i try to create a build for production then it gives me following error:-
ERROR in : Can't resolve all parameters for
UniquePermalinkValidatorDirective in
E:/Manish/Projects/ampleAdmin/src/app/shared/permalink-validation.directive.ts:
([object Object], ?).
permalink-validation.directive.ts
import { Directive } from '#angular/core';
import { AsyncValidator, AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn } from '#angular/forms';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import * as qs from 'qs';
import { PageService } from '../services/page.service';
import { IPage } from '../client-schema';
export function UniquePermalinkValidator(pageService: PageService, page: IPage): AsyncValidatorFn {
return (ctrl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
if (!(ctrl && ctrl.value)) { return null; }
const cond: any = {
where: {
permalink: ctrl.value
}
};
if (page && page.id) {
cond.where.id = { nin: [page.id]};
}
const query = qs.stringify(cond, { addQueryPrefix: true });
return pageService.getPageCount(query).pipe(
map(res => {
return res && res.count ? { uniquePermalink: true } : null;
})
);
};
}
#Directive({
selector: '[appUniquePermalink]',
providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: UniquePermalinkValidatorDirective, multi: true }]
})
export class UniquePermalinkValidatorDirective implements AsyncValidator {
constructor(private pageService: PageService, private page: IPage) { }
validate(ctrl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return UniquePermalinkValidator(this.pageService, this.page)(ctrl);
}
}
page.component.ts
import { Component, OnInit, TemplateRef } from '#angular/core';
import * as _ from 'lodash';
import { NotifierService } from 'angular-notifier';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { IPage } from 'src/app/client-schema';
import { Utils } from 'src/app/shared/utils';
import { PageService } from 'src/app/services/page.service';
import { UniquePermalinkValidator } from 'src/app/shared/permalink-validation.directive';
#Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.css']
})
export class PageComponent implements OnInit {
private notifier: NotifierService;
pageForm: FormGroup;
pageDetail: IPage;
isAddFold = false;
isEditFold = false;
editFoldIndex = -1;
constructor(
private pageService: PageService,
private notifierService: NotifierService,
private modalService: BsModalService,
private formBuilder: FormBuilder,
) {
this.notifier = notifierService;
}
initPageForm() {
this.pageForm = this.formBuilder.group({
name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(250)]],
permalink: ['', [Validators.required], UniquePermalinkValidator(this.pageService, this.pageDetail)],
folds: [
[]
],
data: null,
status: true
});
}
}
I am using single form for Add/Edit page so i have to require records details to allow permalink on editing a page.
is there any way to pass current page details to directive?
Given
export function UniquePermalinkValidator(pageService: PageService, page: IPage): AsyncValidatorFn {
// ...
}
And given
#Directive({
selector: '[appUniquePermalink]',
providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: UniquePermalinkValidatorDirective, multi: true }]
})
export class UniquePermalinkValidatorDirective implements AsyncValidator {
constructor(private pageService: PageService, private page: IPage) {}
// ...
}
And given that IPage is defined solely by
export interface IPage {
id: number;
// ...
}
Then UniquePermalinkValidatorDirective will not work by definition, failing in the manner described.
An interface defines something only in type space, not in the value space and therefore has no runtime manifestation whatsoever. This means it cannot be used in a value position.
In essence, Angular's dependency injection system reads the type of constructor parameters and, when there is a correspondingly named declaration in the value space, it will use that correspondingly named value as the injection token.
For example, the following
import {Injectable} from '#angular/core';
#Injectable() export class Service {
constructor(http: Http) {}
}
can also be written
import {Inject} from '#angular/core';
export class Service {
constructor(#Inject(Http) http: ThisTypeIsArbitraryWithRespectToInjection) {}
}
which means the same thing
Notice how Http is pass as an argument to Inject. But Inject(IPage), where IPage is an interface, is malformed.
A primary purpose of #Inject(ProviderToken) is to allow one to inject a provider orthogonally to the type of the decorated paramater in cases such as yours.
Therefore, you need something like
constructor(#Inject(pageToken) page) {}
Which means one needs to define a token and use it to register a provider that can be injected.
One can, and should, still write
constructor(#Inject(pageToken) page: IPage) {}
In order to give the parameter a type but the type is unrelated to the value injected for the parameter.
For example
import {InjectionToken, NgModule} from '#angular/core';
export const pageToken = new InjectionToken<IPage>('Page');
#NgModule({
providers: [
{
provide: pageToken,
useFactory: functionReturningAPage
}
]
}) export class // ...

How to Inject plain Service/Provider in nest.js

I made a plain typeScript class in nest.js.
JwtTokenService.js
// JwtTokenService.js
import { Injectable, Optional } from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { JwtPayload } from '../modules/auth/interface/jwt-payload.interface';
#Injectable()
export class JwtTokenService {
constructor(private readonly jwtService: JwtService) {}
async generateJWT(payload: object): Promise<string> {
payload['type'] = 'access_token';
const token = this.jwtService.sign({ payload });
return token;
}
}
Now how can I use this in any controller. like user, auth and other.
Register the service in the nest application module:
import { Module } from '#nestjs/common';
import { YourController } from './path-to/your.controller';
import { JwtTokenService } from './path-to/JwtTokenService.service';
#Module({
controllers: [YourController],
providers: [JwtTokenService],
})
export class ApplicationModule {}
Then you can use it in your controller:
import { Controller, Get, Post, Body } from '#nestjs/common';
import { JwtTokenService } from './path-to/JwtTokenService.service';
#Controller('your')
export class YourController {
constructor(private readonly jwtTokenService: JwtTokenService) {}
#Get()
async get() {
// use `this.jwtTokenService`
...
}
}
Nest is using the a DependencyInjection pattern to provide the service to the controller, which is why you need to declare how the service is provided in the application module.

Categories