NestJs : How to implement node.js Post and Get logic in NestJs - nestjs

I'm trying to implement node.js Spotify Authorization flow in NestJs.
But HttpService Post and Get functions doesn't work as in node.js.
Node.js working example:
var request = require('request'); // "Request" library
app.get('/callback', function(req, res) {
var authOptions = {
url: 'https://some-url.com/api/token',
form: {
code: code,
redirect_uri: redirect_uri,
grant_type: 'authorization_code'
},
headers: {
'Authorization': 'Basic ' + (Buffer.from(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
// I'm trying to implement this post in NestJS
request.post(authOptions, function(error, response, body) {
var options = {
url: 'https://api.spotify.com/v1/me',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
request.get(options, function(error, response, body) {
console.log(body);
});
}
I'm using HttpService Post method in NestJS
and that doesn't work:
constructor(private httpService: HttpService) {}
#Get('callback')
callback(#Request() req, #Res() res): any {
let code = req.query.code || null;
const url = 'https://some-url.com/api/token';
const form = {
code: code,
redirect_uri: this.redirect_uri,
grant_type: 'authorization_code'
}
const headers = {
'Authorization': 'Basic ' + (Buffer.from(this.client_id + ':' + this.client_secret))
}
// doesn't work
this.httpService.post( url, form, { headers: headers }).pipe(
map((response) => {
console.log(response);
}),
);
}

In NestJS, you do not need to send req, res object to your function parameter. Nest Js provide build-in decorator for req.body, req.query and req.param as #Body, #Query, and #Param. I write down to call post method and get method. You can also use put, patch, delete, and other methods. Please make a data transfer object file in your module.
for further reference, you can check this: https://docs.nestjs.com/controllers
export class yourController {
constructor(private readonly httpService: HttpService) {}
#Post('your-route-name')
public postMethod(#Body() yourDTO: YourDTOClass): Promise<interface> {
try {
return this.httpService.method(yourDTO);
} catch (err) {
throw new HttpException(err, err.status || HttpStatus.BAD_REQUEST);
}
}
#Get('your-route-name')
find(#Query() query: QueryDTO): Promise<interface> {
try {
return this.httpService.methodName(query);
} catch (err) {
throw new HttpException(err, err.status || HttpStatus.BAD_REQUEST);
}
}
}

You should put return before this.httpService.post(...). Normally you would have to subscribe to the Observable returned by the post method but NestJS handles this for you through the #Get() decorator.

You should prefix your controller with "async" and use "await" followed by "toPromise()"...
constructor(private httpService: HttpService) {}
#Get('callback')
async callback(#Request() req, #Res() res): any {
// ... remaining code here
const response =
await this.httpService.post(url, form, { headers: headers }).toPromise();
return response;
}

Add this imports to the controller:
import { Observable } from 'rxjs';
import { take, tap, map } from 'rxjs/operators';
Then try this:
constructor(private httpService: HttpService) {}
#Get('callback')
callback(#Request() req, #Res() res): Observable<any> {
let code = req.query.code || null;
const url = 'https://some-url.com/api/token';
const form = {
code: code,
redirect_uri: this.redirect_uri,
grant_type: 'authorization_code'
}
const headers = {
'Authorization': 'Basic ' + (Buffer.from(this.client_id + ':' +
this.client_secret))
}
return this.httpService.post( url, form, { headers: headers }).pipe(
// Take first result to complete the observable..
take(1),
// [OPTIONAL] Some debug log to see the response.
tap((response: { data: any }) => {
console.log(`Response: ${JSON.stringify(response.data)}`);
})
// Map the response object to just return its data.
map((response: { data: any }) => response.data),
);
}

Related

Cannot send form data as 'Multipart/formdata'

Cannot send form data as 'Multipart/formdata' Content type in react-native expo app.
when we send formData object in post request in react -native app, we cant get req.body of req.files from node backend
export const saveUserAddVisitor = async data => {
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await axios.post(
`${apiUrl}/api/v1/mobile/apartment/member`,
data,
{
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
},
);
return addVisitorData;
} catch (err) {
return err;
}
};
You can try something like this which works without Axios:
export const saveUserAddVisitor = async data => {
var data = new FormData()
data.append('foo', {
...
})
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await fetch(`${apiUrl}/api/v1/mobile/apartment/member`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data'
},
body: data
})
return addVisitorData;
} catch {
return err;
}
}
After I gave you an example of working code for the client, but the problem may be from the server ;)
Your code with axios look like fine, just be sure to send FormData type, like RĂ©mi said here https://stackoverflow.com/a/72454168/16205278
You can construct your FormData before this function and use it directly in your current code with your axios function.
Service :
import axios from "axios";
import configConstants from "./config.js";
/**
* Current function to save User Add Visitor
* #param {*} data
*/
export const saveUserAddVisitor = async (data) => {
try {
const apiUrl = configConstants.apiUrlWithPort;
const addVisitorData = await axios.post(
`${apiUrl}/api/v1/mobile/apartment/member`,
data,
{
headers: {
Accept: 'application/json',
"Content-Type": "multipart/form-data"
}
}
);
return addVisitorData;
} catch (err) {
return err;
}
};
Use :
import {saveUserAddVisitor} from "./index"
const form = new FormData();
form.append("visitor", { firstName: "Jack", lastName: "Doe" });
saveUserAddVisitor(form);
API Express :
Apparently, express can't resolve multipart-form data unless some help, according following ressource : https://codex.so/handling-any-post-data-in-express
You have to use multer middleware :
const multer = require('multer');
app.post('/', multer().none(), function (req, res, next) {
req.body;
//... some code
});

How can I send different request in intercepter?

I'm trying to send different request in interceptor
I want to send accessToken in request header authorization for every request except one case
so I write this code in interceptor request.use
config.headers = {authorization: `Bearer ${accessToken}`};
but if this error occurred
error.response.data.code === 'expired'
I want to send refreshtoken in header authorization not accesstoken
so I write this code in interceptor.response.use.error
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
this is my code
useEffect(() => {
axios.interceptors.request.use(async (config: any) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
config.headers = { authorization: `Bearer ${accessToken}` };
return config;
});
axios.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const {
config,
response: { status },
} = error;
if (status === 419) {
if (error.response.data.code === "expired") {
const originalRequest = config;
const refreshToken = await EncryptedStorage.getItem("refreshToken");
const { data } = await axios.post(
`${Config.API_URL}/user/refreshToken`,
{},
{ headers: { authorization: `Bearer ${refreshToken}` } }
);
return axios(originalRequest);
}
}
return Promise.reject(error);
}
);
}, [dispatch]);
how can i fix my code?
if i use my code if error.response.data.code === 'expired'
the headers.authorization accesstoken is still being requested.
Make it so your request interceptor only sets a default authorization header without overriding anything already present
axios.interceptors.request.use(async (config) => {
const accessToken = await EncryptedStorage.getItem("accessToken");
return {
...config,
headers: {
authorization: `Bearer ${accessToken}`,
...config.headers
}
}
});
You could also avoid making the getItem() request entirely which might save a little time
axios.interceptors.request.use(async (config) => {
if (!config.headers.authorization) {
config.headers.authorization = `Bearer ${await EncryptedStorage.getItem("accessToken")}`
}
return config;
});

i'm getting isssue while calling API in Useeffect in reactJs

I'm calling two methods in useeffect but when api1 get called it get executed till await axios.put and then api2 get called without getting the response from api1. And after getting a reponse from api2 it goes to api1 reponse and gets a reponse as undefined
useEffect(() => {
api1();
api2();
}, [])
const api1 = async () => {
try {
var requestModel = JSON.stringify({ UserId: userid, MenuName: menuname });
var requestBody = security.encrypt(requestModel);
axios.myHashData = security.computedHmac256(requestBody);
var config = { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }
await axios.put(axios.controllername + 'methodname', requestBody, config, { withCredentials: true })
.then(response => {
if (response.status === 200) {
//setapidata(response.data);
}
});
} catch (error) {
console.error(error.message)
}
}
const api2= async () => {
try {
var requestModel = JSON.stringify({ UserID: userid });
var requestBody = security.encrypt(requestModel);
axios.myHashData = security.computedHmac256(requestBody);
var config = { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } }
await axios.post(axios.controllername + 'methodname', requestBody, config, { withCredentials: true })
.then(response => {
if (response.status === 200) {
setapidata(response.data);
GetApis();
}
});
} catch (error) {
console.error(error.message)
}
}
If you want to wait for the api1 to execute before api2 is called you need to change the code for useEffect as below
useEffect(async() => {
await api1();
await api2();
}, [])
or
useEffect(() => {
api1().then(() => api2());
}, [])
To handle errors properly use try-catch block inside the useEffect if using await or use catch block if using .then
Also, another suggestion, if you are using a function inside useEffect make sure either the function is defined inside the same useEffect block or it is memorized either via useCallback or useMemo

Send File from Angular to Nodejs - Cannot read property 'headers' of undefined

I'm having issues passing my data with a file to my nodejs backend. I'm currently using azure functions to run my nodejs code. Currently when I pass the data with file, I'm getting a Cannot read property 'headers' of undefined I'm adding the header in the options so I don't really understand why I'm getting the error.` Working with files is definitely one of my weaknesses so I appreciate any help!
import { Injectable, OnDestroy } from "#angular/core";
import { Subject, Observable } from "rxjs";
import {
HttpClient,
HttpParams,
HttpRequest,
HttpHeaders,
HttpEvent,
HttpEventType
} from "#angular/common/http";
import { map, takeUntil, switchMap } from "rxjs/operators";
import { Router } from "#angular/router";
import { environment } from 'src/environments/environment';
import { AuthService } from '../auth.service';
import { SendAppealModel } from './send-appeal.model';
#Injectable({ providedIn: "root" })
export class SubmitAppealService implements OnDestroy {
destroy = new Subject();
constructor(private http: HttpClient, private router: Router, private authService: AuthService) { }
ngOnDestroy() {
this.destroy.next();
this.destroy.complete();
}
submitAppeal(
username: string,
email: string,
file: File
) {
let form = new FormData();
form.append('file', file);
form.append('username', username);
form.append('email', email);
console.log("FILE OUTPUT");
console.log(file);
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
const api = environment.azure_function_url + `/PATCH-Send-Appeal`;
const req = new HttpRequest('PATCH', api, form, options);
return this.http.request(req)
.pipe(
map((res: HttpEvent<any>) => {
if (res.type === HttpEventType.Response) {
return res.body.id.toString();
} else if (res.type === HttpEventType.UploadProgress) {
// Compute and show the % done:
const UploadProgress = +Math.round((100 * res.loaded) / res.total);
return UploadProgress;
}
})
);
}
}
azure function
const multer = require('multer');
const upload = multer({ dest: 'public/uploads/' }).single('file');
module.exports = function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
upload();
console.log(req.file);
var filename = path.basename("../" + req.file.path);
console.log("filename");
console.log(req.file.destination);
console.log(__dirname);
var form = new formidable.IncomingForm();
console.log("form");
console.log(form);
context.res = {
status: 200,
headers: {
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Set-Cookie',
'Access-Control-Max-Age': '86400',
Vary: 'Accept-Encoding, Origin',
'Content-Type': 'application/json',
},
};
context.done();
};
I'm assuming you are getting that error because your headers aren't actually making it to your azure function.
Currently you have this:
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
You can't do that. headers.append doesn't do an in-place update. It returns a new HttpHeaders object. So, you actually need this:
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'multipart/form-data');
headers = headers.append('Accept', 'application/json');
let options = { headers: headers, reportProgress: true };
Per comments, I see one other thing that looks a little off to me. This may be part of the issue. Try updating your HTTP call to this:
const req = new HttpRequest('PATCH', api, form, options);
return this.http.patch(api, form, options)
.pipe(
map((res: HttpEvent<any>) => {
if (res.type === HttpEventType.Response) {
return res.body.id.toString();
} else if (res.type === HttpEventType.UploadProgress) {
// Compute and show the % done:
const UploadProgress = +Math.round((100 * res.loaded) / res.total);
return UploadProgress;
}
})
);
You might also set a breakpoint in your azure function on the first line to inspect the request object and make sure your HttpHeaders are making it in.

How to pass parameters in headers

I need to call an API from my Angular Node application.For that i have to pass username and password along with header request.How to pass parameters?
I tried below code
From Angular service,
checkUserApi(url: string): Observable<any> {
const headers = new HttpHeaders()
.set('Authorization', 'my-auth-token')
.set('Content-Type', 'application/json')
.set('userName','prismtest')
.set('password','12345678');
return this.http.post('/upload/checkUserApi/', { headers })
.map((response: Response) => {
console.log(response);
return response;
})
}
In Node,
router.post('/checkUserApi',function(req,res,next){
var proxyUrl = 'http://cors-anywhere.herokuapp.com/';
Request.post({
"headers": { "userName": "prismtest","password":"12345678" },
"url": proxyUrl+"https://example.com",
"body": {}
}, (error, response, body) => {
if(error) {
return console.log(error);
}
res.send(response);
});
});
You can pass the form parameters as the second attribute to post request:
checkUserApi(url: string): Observable<any> {
const headers = new HttpHeaders()
.set('Authorization', 'my-auth-token')
.set('Content-Type', 'application/json');
return this.http.post('/upload/checkUserApi/', {username: 'prismtest', password: '123'}, { headers })
.map((response: Response) => {
console.log(response);
return response;
})
}
You can do it by using interceptors which is Angular Service.
this should be helpfull :How to pass a param to HttpInterceptor?
Can you try below code following
/* SERVICE ANGULAR*/
import {Injectable} from '#angular/core';
import {HttpClient, HttpHeaders} from '#angular/common/http';
import {Observable,of} from 'rxjs';
import { map,catchError } from 'rxjs/operators';
import {User} from '../_models';
const httpOptions = {
headers:new HttpHeaders({'Content-Type':'application/json'})
};
const API_URL = "http://localhost/app/api";
#Injectable({
providedIn:'root',
})
export class UserService{
constructor(private http:HttpClient){}
/**
* POST LOGIN USER
*/
login(user: any) {
return this.http.post<any>(API_URL+"/login", user,httpOptions)
.pipe(map(user => {
return user;
}));
}
}
/*NODEJS*/
/* 1) install $ npm install body-parser --save*/
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies */
/ POST http://localhost:8080/api/users
// parameters sent with
app.post('/api/login', function(req, res) {
var user_id = req.body.id;
var token = req.body.token;
var geo = req.body.geo;
res.send(user_id + ' ' + token + ' ' + geo);
});
Can you seen : Use ExpressJS to Get URL and POST Parameters
Login State Angular + Laravel
const headerOptions = {
headers : new HttpHeaders({
'Constent-Type' : 'application/json',
'Authorization : 'you token here'
})
}
http.post('you action', {body Params}, headerOptions, function(res){
console.log('your respose is ', res);
})

Resources