How to use Axios in Nest.js to POST params request? - axios

I am beginner in NestJS and I would like to ask question on how to use Axios in NestJs. But the problem is I keep getting error TypeError: Converting circular structure to JSON
I have created an API POST http://localhost:3000/api/create in NestJS and my raw request
Raw input
{
"id":1,
"date":"2022-06-28",
"roster":[
{
"startDate":"2022-06-27",
"weekDay":1,
"session":1,
"sTime":"08:00:00",
"eTime":"10:00:00"
}
]
}
Postman request images
My goal is in create.service.ts file, I want pass the value that I receive in postman and add all values into new API link https://create.com/api/index.php?id=&date=&roster=[].
Expected output : https://create.com/api/index.php?id=1&date=2022-06-27&roster=[{"startDate":"2022-06-27","weekDay":1,"session":1,"sTime":"08:00:00","eTime":"10:00:00"}]
But I receive an error TypeError: Converting circular structure to JSON when I submit my post request
create.service.ts
import { HttpException, Injectable } from '#nestjs/common';
import axios from 'axios';
import { CreateValidator } from './create.validator';
#Injectable()
export class CreateService {
constructor() { }
async create(query: CreateValidator) {
return axios.post(`https://create.com/api/index.php?`, null, { params: query })
.then()
.catch(err => {
throw new HttpException(err.response?.data ?? err.response?.statusText ?? 'Unexpected error.', err.response?.status ?? 400);
});
}
}
create.controller.ts
import { Post, Body } from '#nestjs/common';
import { ApiConsumes, ApiTags } from '#nestjs/swagger';
import { BaseController } from '../base/base.controller';
import { CreateService } from './create.service';
import { CreateValidator } from './create.validator';
#ApiTags('New : Create ')
export class CreateController extends BaseController {
constructor(
private readonly createService: CreateService,
) {
super();
}
#Post('api/create')
#ApiConsumes('application/x-www-form-urlencoded')
async create(#Body() body: CreateValidator) {
const result = await this.createService.create(body);
return {result,};
}
}
create.validator.ts
import { ApiProperty } from '#nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsNotEmpty, isString } from 'class-validator';
export class CreateValidator {
#ApiProperty({type: Number,required: true,})
id: number;
#ApiProperty({type: String,required: true,})
date: Date;
#ApiProperty({type: [Object],})
#Transform((input) => isString(input.value) ? JSON.parse(input.value) : input.value)
#IsNotEmpty({ message: 'IsNotEmpty' })
roster: Array<{
startDate: Date;
weekDay: number;
s_time: string;
e_time: string;
}>;
}
My current coding as below

It turns out that I need to change #ApiProperty({ type : [Object]}) into #ApiProperty({ type : Object }) in my validator file.
create.validator.ts
import { ApiProperty } from '#nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsNotEmpty, isString } from 'class-validator';
export class CreateValidator {
#ApiProperty({type: Number,required: true,})
id: number;
#ApiProperty({type: String,required: true,})
date: Date;
#ApiProperty({type: Object,})
#Transform((input) => isString(input.value) ? JSON.parse(input.value) : input.value)
#IsNotEmpty({ message: 'IsNotEmpty' })
roster: Array<{
startDate: Date;
weekDay: number;
s_time: string;
e_time: string;
}>;
}
Now it's working fine.
Thanks.

Related

Trouble with module import [ NEST JS ]

I'm trying to use a jwt.strategy.ts to protect my endpoints with jwt verification. Everything was going ok until I decided to import a custom JWTService inside this jwt.strategy which contains its own JWTModule but Nest doesn't seems to recognize it. I can use the JWTService in other services but it doesn't work inside the strategy. What should I do ? What am I doing wrong ?
The NEST Message:
[Nest] 53 - 09/22/2022, 6:32:25 PM ERROR [ExceptionHandler] Nest can't resolve dependencies of the JwtStrategy (ConfigService, ?). Please make sure that the argument Object at index [1] is available in the AuthModule context.
Potential solutions:
- If Object is a provider, is it part of the current AuthModule?
- If Object is exported from a separate #Module, is that module imported within AuthModule?
#Module({
imports: [ /* the Module containing Object */ ]
})
Error: Nest can't resolve dependencies of the JwtStrategy (ConfigService, ?). Please make sure that the argument Object at index [1] is available in the AuthModule context.
Potential solutions:
- If Object is a provider, is it part of the current AuthModule?
- If Object is exported from a separate #Module, is that module imported within AuthModule?
#Module({
imports: [ /* the Module containing Object */ ]
})
The JWTModule:
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { jwtOptions } from './jwt.config';
import { JWTService } from './jwt.service';
#Module({
imports: [JwtModule.registerAsync(jwtOptions)],
providers: [JWTService],
exports: [JWTService],
})
export class JWTModule {}
The JWTService:
import { Request } from 'express';
import { DecodeOptions } from 'jsonwebtoken';
import { Injectable, UnprocessableEntityException } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { JwtService, JwtSignOptions, JwtVerifyOptions } from '#nestjs/jwt';
import { CookieHttpConfig } from '../auth';
#Injectable()
export class JWTService {
constructor(
private readonly jwtService: JwtService,
private readonly configService: ConfigService,
) {}
sign(payload: string | object | Buffer, options?: JwtSignOptions) {
return this.jwtService.sign(payload, options);
}
async signAsync(payload: string | object | Buffer, options?: JwtSignOptions) {
return this.jwtService.signAsync(payload, options);
}
verify(token: string, options?: JwtVerifyOptions) {
return this.jwtService.verify(token, options);
}
async verifyAsync(token: string, options?: JwtVerifyOptions) {
return this.jwtService.verifyAsync(token, options);
}
decode(token: string, options?: DecodeOptions) {
return this.jwtService.decode(token, options);
}
async getToken(tokenPayload: any): Promise<string> {
try {
const token: string = await this.jwtService.signAsync(tokenPayload);
return `Bearer ${token}`;
} catch (error) {
throw new UnprocessableEntityException(error.message);
}
}
async refreshToken(
cookieName: string,
request: Request,
payload: any,
): Promise<void> {
const token: string = await this.getToken(payload);
request.res.cookie(cookieName, token, CookieHttpConfig.Options());
}
}
jwt.strategy:
import { JWTService } from '#app/common';
import { Injectable } from '#nestjs/common';
import { ConfigService } from '#nestjs/config';
import { PassportStrategy } from '#nestjs/passport';
import { Request } from 'express';
import { ExtractJwt, Strategy } from 'passport-jwt';
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
private readonly configService: ConfigService,
private readonly jwtService: JWTService,
) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
try {
const token = request.signedCookies['Authorization'].split(' ')[1];
return token;
} catch (error) {
return null;
}
},
]),
ignoreExpiration: false,
secretOrKey: configService.get('AUTH_JWT_SECRET'),
});
}
async validate(request: Request, payload: any): Promise<any> {
const tokenPayload = {
email: payload.email,
id: payload.id,
};
await this.jwtService.refreshToken('Authorization', request, tokenPayload);
return tokenPayload;
}
}
Extra information about my project:
I divided the project into a monorepo, so I imported the JWTModule inside the AuthModule but it still doesn't work. The jwt.strategy.ts and the JWTModule is inside a shared lib created at the same level as the apps folders containing the microservices.
The AuthModule:
import { Module } from '#nestjs/common';
import { PassportModule } from '#nestjs/passport';
import { UsersModule } from '../users/users.module';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import {
AuthLibModule,
JWTModule,
JwtStrategy,
LocalStrategy,
} from '#app/common';
import { Web3Module } from '../web3/web3.module';
import { VonageModule } from '#app/common';
#Module({
imports: [
UsersModule,
PassportModule,
JWTModule,
Web3Module,
VonageModule,
AuthLibModule,
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy],
})
export class AuthModule {}

(node:18560) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'typeFn' of undefined

I am getting this error when I am trying to resolve a field(blocks) with the #ResolveField() decorator.
page.resolver.ts
import {
Resolver,
Query,
Mutation,
Args,
ResolveField,
Parent,
} from '#nestjs/graphql';
import { PageService } from './page.service';
import { PageType } from './type/page.type';
import { CreatePageInput } from './input/create-page.input';
import { BlockService } from '../block/block.service';
import { Page } from './page.interface';
#Resolver('Page')
export class PageResolver {
constructor(
private readonly pageService: PageService,
private readonly blockService: BlockService,
) {}
#Query(() => [PageType])
pages() {
return this.pageService.getAllPages();
}
#Query(() => [PageType])
async page(#Args('id') id: string) {
return this.pageService.getPage(id);
}
#Mutation(() => PageType)
createPage(#Args('createPageInput') createPageInput: CreatePageInput) {
return this.pageService.createPage(createPageInput);
}
#ResolveField()
blocks(#Parent() page: Page) {
return this.blockService.getManyBlocks(page.blockIds);
}
}
page.interface.ts
import { Document } from 'mongoose';
export interface Page extends Document {
readonly id: string;
readonly name: string;
readonly createdAt: Date;
readonly updatedAt: Date;
readonly createdBy: string;
readonly updatedBy: string;
readonly blockIds: string[];
}
It's solved after using the function () => PageType in the #Resolver() decorator.
#Resolver(() => PageType)

#nestjs/graphql Error: You need to provide explicit type for DemandResolver#demand parameter #0

I'm creating a graphql server with nestjs + graphql + typeORM + mongoDB.
I just can't do a query retrieving a request with an ObjectID (mongoDB id).
And with the example below i Have this error
UnhandledPromiseRejectionWarning: Error: You need to provide explicit type for DemandResolver#demand parameter #0 !
objectId.scalar.ts
import {Kind} from 'graphql';
import {CustomScalar, Scalar} from '#nestjs/graphql';
import {ObjectID} from 'typeorm';
#Scalar('ObjectID', type => ObjectID)
export class ObjectIDScalar implements CustomScalar<string, ObjectID> {
description = 'Mongo object id scalar type';
parseValue(value: string) {
return new ObjectID(value); // value from the client input variables
}
serialize(value: ObjectID) {
return value.toHexString(); // value sent to the client
}
parseLiteral(ast) {
if (ast.kind === Kind.STRING) {
return new ObjectID(ast.value); // value from the client query
}
return null;
}
}
demand.ts
import {Field, InputType, ObjectType} from 'type-graphql';
import {Column, Entity, ObjectID, ObjectIdColumn} from 'typeorm';
#Entity()
#ObjectType()
#InputType('DemandInput')
export class Demand {
#Field(type => ObjectID, { nullable: true })
#ObjectIdColumn()
id?: ObjectID;
#Field({ nullable: true })
#Column({ nullable: true })
name?: string;
}
demand.module.ts
import {Module} from '#nestjs/common';
import {DemandResolver} from './demand.resolver';
import {DemandService} from './demand.service';
import {TypeOrmModule} from '#nestjs/typeorm';
import {Demand} from './demand';
import {ObjectIDScalar} from '../scalars/objectId.scalar';
#Module({
imports: [TypeOrmModule.forFeature([Demand])],
providers: [DemandResolver, DemandService, ObjectIDScalar],
})
export class DemandModule {}
demand.resolver.ts
import {Args, Mutation, Query, Resolver} from '#nestjs/graphql';
import {Demand} from '/demand';
import {DemandService} from './demand.service';
import {ObjectID} from 'typeorm';
#Resolver(Demand)
export class DemandResolver {
constructor(private readonly demandService: DemandService) { }
#Query(() => Demand)
async demand(#Args('id') id: ObjectID) {
return await this.demandService.findById(id);
}
}
demand.service.ts
import {Injectable} from '#nestjs/common';
import {InjectRepository} from '#nestjs/typeorm';
import {Demand} from '/demand';
import {MongoRepository, ObjectID} from 'typeorm';
#Injectable()
export class DemandService {
constructor(
#InjectRepository(Demand) private readonly demandRepository: MongoRepository<Demand>,
) {}
async findById(id: ObjectID) {
return this.demandRepository.findOne(id);
}
}
I hope you can help me to understand. I try to replace ObjectID by string in the resolver and service and it works but i have an another error
#Query(() => Demand)
async demand(#Args('id') id: string) {
return await this.demandService.findById(id);
}
UnhandledPromiseRejectionWarning: Error: Cannot determine GraphQL output type for id

Cannot read property forEach of undefined

The title of this question is just the error I am currently receiving, but what I really need help with is understanding observables and API calls. For whatever reason, I just haven't been able to get a good grasp of this concept, and I am hoping that someone might have an explanation that will finally click.
I am trying to create a new Angular service that retrieves JSON from an API. I then need to map the response to a model. Due to weird naming conventions, job descriptions and job requirements are used interchangeably here. Here is my service class.
import { CommunicationService } from './communication.service';
import { AiDescription } from '../models/ai-description.model';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class AiDescriptionService {
requirements: Observable<AiDescription[]>;
private aiDescriptionUrl: string = '/api/core/company/jobdescriptions';
private dataStore: {
requirements: AiDescription[]
};
private _requirements: BehaviorSubject<AiDescription[]>;
private emptyRequestParams = {
"company_id": "",
"carotene_id": "",
"carotene_version": "",
"city": "",
"state": "",
"country": ""
};
readonly caroteneVersion: string = "caroteneV3";
constructor(
private communicationService: CommunicationService
) {
this.dataStore = { requirements: [] };
this._requirements = new BehaviorSubject<AiDescription[]>([]);
this.requirements = this._requirements.asObservable();
}
LoadRequirements(params: Object) {
this.communicationService.postData(this.aiDescriptionUrl, params)
.subscribe(res => {
let jobDescriptions = [];
jobDescriptions = res.jobdescriptions;
jobDescriptions.forEach((desc: { id: string; description: string; }) => {
let aiDescription = new AiDescription();
aiDescription.id = desc.id;
aiDescription.description = desc.description;
});
this.dataStore.requirements = res;
this._requirements.next(Object.assign({}, this.dataStore).requirements);
});
}
CreateRequest(
companyID : string,
caroteneID : string,
city: string,
state: string,
country: string
): Object {
let newRequestParams = this.emptyRequestParams;
newRequestParams.company_id = companyID;
newRequestParams.carotene_id = caroteneID;
newRequestParams.carotene_version = this.caroteneVersion;
newRequestParams.city = city;
newRequestParams.state = state;
newRequestParams.country = country;
this.LoadRequirements(newRequestParams);
return this.dataStore;
}
}
The postData() function being called by this.communicationService is here:
postData(url: string, jobInformation: any): Observable<any> {
const start = new Date();
const headers = new HttpHeaders({ 'Content-Type': 'application/json' });
const body = JSON.stringify(jobInformation);
const options = { headers };
return this.http.post(url, body, options)
.catch(err => Observable.throw(err))
.do(() => {
this.analyticsLoggingService.TrackTiming('JobPostingService', 'PostSuccess', new Date().getTime() - start.getTime());
}, () => {
this.analyticsLoggingService.TrackError('JobPostingService', 'PostFailure');
});
}
I didn't write the postData function, and I would not be able to modify it. When running a unit test, I am getting this error: "TypeError: Cannot read property 'forEach' of undefined".
But more than simply fixing the error, I am really trying to get a better understanding of using Observables, which is something I haven't been able to get a good understanding of from other sources.
In your example, I recommend replacing any and Object with explicitly defined models.
Here's an example for Angular 8 for Subscription, Promise, and Observable API calls. You can get more info here: https://angular.io/tutorial/toh-pt6.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs';
import { User } from './user.model';
#Injectable({ providedIn: 'root' })
export class UserService {
users: User[];
authHeaders = new HttpHeaders()
.set('Content-Type', 'application/json');
constructor(
private readonly http: HttpClient
) { }
getUsers() {
this.http.get(`https://myApi/users`, { headers: this.authHeaders })
.subscribe(
(data: User[]) => {
this.users = data;
}, (error: HttpErrorResponse) => { /* handle error */ });
}
async getUserPromise(userID: number): Promise<User> {
const url = `https://myApi/users/${userID}`;
return this.http.get<User>(url, { headers: this.authHeaders })
.toPromise();
}
getUserObservable(userID: number): Observable<User> {
const url = `https://myApi/users/${userID}`;
return this.http.get<User>(url, { headers: this.authHeaders });
}
}
I like to keep my class models in separate files. This example would have user.model.ts with content like:
export class User {
constructor(
public id: number,
public username: string,
public displayName: string,
public email: string
) { }
}
I've not included authentication headers or error handling for brevity; however, you might want to add those as needed.

angular2-mdl table component with server side data

I experiment with Angular 2 - Material Design Lite especially with the table component but I can not figure out how would I pass data from server on ajax request. Here is the example provided for table initialisation.
How would I pass data from restAPI to table component?
Here I have a kind of working example. I placed the initial data on my Component Init method where I call the DataService which populates the table. I'm not sure if is the right workaround but at this point I have data in table.
import { Component, ViewChild, ViewContainerRef, OnInit, Pipe, PipeTransform } from '#angular/core';
import { MdDialog, MdDialogConfig, MdIcon } from "#angular/material";
import { AuthenticationService, DialogsService, DataService } from '../../../services/';
import { RouterModule, Routes, Router } from '#angular/router';
import {
IMdlTableModelItem,
MdlDefaultTableModel
} from 'angular2-mdl';
export interface ITableItem extends IMdlTableModelItem {
username: string;
email: string;
role: string;
unitPrice: number;
}
#Component({
selector: 'employees',
templateUrl: 'app/layouts/secure/employees/employees.html',
providers: [DialogsService, MdIcon]
})
export class EmployeesComponent implements OnInit {
public message: string;
public employees: any[];
public result: any;
public showSearchBar: false;
public tableData:[ITableItem];
public selected;
public tableModel = new MdlDefaultTableModel([
{key:'username', name:'Username', sortable:true},
{key:'email', name:'Email', sortable:true},
{key:'role', name:'Role', sortable:true},
{key:'status', name:'Status', sortable:true},
{key:'unitPrice', name:'Test', numeric:true}
]);
constructor(
private dialogsService: DialogsService,
public viewContainerRef: ViewContainerRef,
private _dataService : DataService,
private router: Router
) {
}
openDialog() {
this.dialogsService
.confirm('User Form', 'Are you sure you want to do this?', this.viewContainerRef)
.subscribe(res => this.result = res);
}
toggleSearch() {
console.log(this)
}
ngOnInit() {
var self = this;
this._dataService
.GetAll('employees')
.subscribe( data => {
data = Object.keys(data).map((key)=>{ return data[key]})
this.employees = data;
this.tableData = data;
this.tableModel.addAll(this.tableData);
}, error => console.log(error),
() => function ( data ) {
this.tableData = this.employees;
this.tableModel.addAll(this.tableData);
this.selected = this.tableData.filter( data => data.selected);
},
);
}
generateArray(obj){
return Object.keys(obj).map((key)=>{ return obj[key]});
}
selectionChanged($event){
this.selected = $event.value;
}
}
#fefe made it a little more difficult than it had to be, at least with the current version. The magic of the as keyword can do the heavy lifting.
For example my class setup looks like:
import...
export interface IUnreadMessage extends IMdlTableModelItem {
messageId: number;
subject: string;
from: string;
}
#Component ...
export class ...
private unreadMessagesTable = new MdlDefaultTableModel([
{key: 'messageId', name: 'Message ID'},
{key: 'subject', name: 'Subject'},
{key: 'from', name: 'From'}
]);
Then in my ajax call I have:
...ajax call here).subscribe(value => {
const messages = value as Array<IUnreadMessage>;
this.unreadMessagesTable.addAll(messages);
},
error => {
...error handler here...
});
Make sure your interface is EXACTLY (including case) the same as your returned ajax data and it should hook right up!