Cannot GET /[object%20Object] when calling axios.get() - node.js

When I paste the endpoint URL with query directly inside the axios.get(), it responds correctly and I can see the json object returned. (i.e axios.get(http://localhost:3000/api/products/product_search?secretKey=${secret}&id=${blabla})). However, if I call the url with the summonerByNameUrl method, it crashes when I make a request. What is the problem in my code?
Crash report:
...
data: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>Cannot GET /[object%20Object]</pre>\n' +
'</body>\n' +
'</html>\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Code:
config.js
const summonerByNameUrl = (summonerName) => `${URL(hidden)}${summonerName}`;
module.exports = {
summonerByNameUrl
}
summoner.js
const config = require('../config');
const axios = require('axios');
const getSummonerByName = async (summonerName) => {
const res = await axios.get(config.summonerByNameUrl(summonerName));
return res.data;
}
const summonerParser = async (req, res) => {
if(!req.query.secretKey)
return res.status(403).json({error: 'missing secret key.'})
let data = await getSummonerByName(req.query)
return res.status(200).json(data);
}
module.exports = {
getSummonerByName,
summonerParser
}
products.js
var express = require('express');
var axios = require('axios')
var router = express.Router();
const summoner = require('../services/summoner');
router.get('/product_search', summoner.summonerParser)
module.exports = router;
app.js
...
app.use('/api/products', productsRouter);
...

You're calling your function with getSummonerByName(req.query) where it is clear from the lines just before that req.query is an object and not a string. When objects are used in a string-context (like your URL), they become "[object Object]", hence the error.
Taking some guesses here but it seems you want to forward some req.query information to the Axios call as query params. Try this instead...
const PRODUCT_SEARCH_URL = "http://localhost:3000/api/products/product_search"
const getSummonerByName = async ({ secretKey, id }) => {
const { data } = await axios.get(PRODUCT_SEARCH_URL, {
params: { secretKey, id }
})
return data
}
If you've got a helper function that returns the base URL (ie http://localhost:3000/api/products/product_search) then by all means, use that instead of a string literal in the Axios call.

The req.query is a Object, not a string.
You can try map the req.query object to make a string. Something like that:
Object.keys(req.query).map(key => {
return key + '=' + req.query[key]
}).join('&')
This code return a string like that: 'id=1&name=test', so you can pass to the endpoint.

Related

Unable to stub an exported function with Sinon

I need to test the following createFacebookAdVideoFromUrl() that consumes a retryAsyncCall that I'd like to stub with Sinon :
async function createFacebookAdVideoFromUrl(accountId, videoUrl, title, facebookToken = FACEBOOK_TOKEN, options = null, businessId = null) {
const method = 'POST';
const url = `${FACEBOOK_URL}${adsSdk.FacebookAdsApi.VERSION}/${accountId}/advideos`;
const formData = {
access_token: businessId ? getFacebookConfig(businessId).token : facebookToken,
title,
name: title,
file_url: videoUrl,
};
const callback = () => requestPromise({ method, url, formData });
const name = 'createFacebookAdVideoFromUrl';
const retryCallParameters = buildRetryCallParameters(name, options);
const adVideo = await retryAsyncCall(callback, retryCallParameters);
logger.info('ADVIDEO', adVideo);
return { id: JSON.parse(adVideo).id, title };
}
This retryAsyncCall function is exported as such:
module.exports.retryAsyncCall = async (callback, retryCallParameters, noRetryFor = [], customRetryCondition = null) => {
// Implementation details ...
}
Here is how I wrote my test so far:
it.only("should create the video calling business's Facebook ids", async () => {
const payload = createPayloadDataBuilder({
businessId: faker.internet.url(),
});
const retryAsyncCallStub = sinon.stub(retryAsyncCallModule, 'retryAsyncCall').resolves('random');
const createdFacebookAd = await FacebookGateway.createFacebookAdVideoFromUrl(
payload.accountId,
payload.videoUrl,
payload.title,
payload.facebookToken,
payload.options,
payload.businessId,
);
assert.strictEqual(retryAsyncCallStub.calledOnce, true);
assert.strictEqual(createdFacebookAd, { id: 'asdf', title: 'asdf' });
});
I don't expect it to work straightaway as I am working in TDD fashion, but I do expect the retryAsyncCall to be stubbed out. Yet, I am still having this TypeError: Cannot read property 'inc' of undefined error from mocha, which refers to an inner function of retryAsyncCall.
How can I make sinon stubbing work?
I fixed it by changing the way to import in my SUT :
// from
const { retryAsyncCall } = require('../../../helpers/retry-async');
// to
const retry = require('../../../helpers/retry-async');
and in my test file :
// from
import * as retryAsyncCallModule from '../../../src/common/helpers/retry-async';
// to
import retryAsyncCallModule from '../../../src/common/helpers/retry-async';
The use of destructuring seemed to make a copy instead of using the same reference, thus, the stub was not applied on the right reference.

How to make a GET Request for a unique register with AXIOS and NodeJS/Express

I'm trying to make GET request to external API (Rick and Morty API). The objective is setting a GET request for unique character, for example "Character with id=3". At the moment my endpoint is:
Routes file:
import CharacterController from '../controllers/character_controller'
const routes = app.Router()
routes.get('/:id', new CharacterController().get)
export default routes
Controller file:
async get (req, res) {
try {
const { id } = req.params
const oneChar = await axios.get(`https://rickandmortyapi.com/api/character/${id}`)
const filteredOneChar = oneChar.data.results.map((item) => {
return {
name: item.name,
status: item.status,
species: item.species,
origin: item.origin.name
}
})
console.log(filteredOneChar)
return super.Success(res, { message: 'Successfully GET Char request response', data: filteredOneChar })
} catch (err) {
console.log(err)
}
}
The purpose of map function is to retrieve only specific Character data fields.
But the code above doesn't work. Please let me know any suggestions, thanks!
First of all I don't know why your controller is a class. Revert that and export your function like so:
const axios = require('axios');
// getCharacter is more descriptive than "get" I would suggest naming
// your functions with more descriptive text
exports.getCharacter = async (req, res) => {
Then in your routes file you can easily import it and attach it to your route handler:
const { getCharacter } = require('../controllers/character_controller');
index.get('/:id', getCharacter);
Your routes imports also seem off, why are you creating a new Router from app? You should be calling:
const express = require('express');
const routes = express.Router();
next go back to your controller. Your logic was all off, if you checked the api you would notice that the character/:id endpoint responds with 1 character so .results doesnt exist. The following will give you what you're looking for.
exports.getCharacter = async (req, res) => {
try {
const { id } = req.params;
const oneChar = await axios.get(
`https://rickandmortyapi.com/api/character/${id}`
);
console.log(oneChar.data);
// return name, status, species, and origin keys from oneChar
const { name, status, species, origin } = oneChar.data;
const filteredData = Object.assign({}, { name, status, species, origin });
res.send(filteredData);
} catch (err) {
return res.status(400).json({ message: err.message });
}
};

pass variable to nodesjs to localhost

I want to pass the variable "valor_dollar" into the server localhost or an file html. Actually I can not to look the variable the browser.
const express = require ('express');
const cheerio = require ('cheerio')
const request = require ('request-promise')
const axios = require('axios')
const app = express();
app.get('/',(req,res) => {
res.end(prova());
});
app.listen(3000);
function prova(){
axios.get('https://www.morningstar.es/es/funds/snapshot/snapshot.aspx?id=F0GBR04ASX').then((response) => {
// Load the web page source code into a cheerio instance
const $ = cheerio.load(response.data)
// The pre.highlight.shell CSS selector matches all `pre` elements
// that have both the `highlight` and `shell` class
const urlElems = $('td.line.text')[0]
const urlText = $(urlElems).text()
let valor_dollar = Number(urlText.substring(4,7))
console.log(valor_dollar)
})
}
You are calling res.end method directly without setting the body, the prova function returns undefined.
Instead you can modify your method like this to have a callback that runs after the HTTP call.
function prova(callback){
axios.get('https://www.morningstar.es/es/funds/snapshot/snapshot.aspx?id=F0GBR04ASX').then((response) => {
// Load the web page source code into a cheerio instance
const $ = cheerio.load(response.data)
// The pre.highlight.shell CSS selector matches all `pre` elements
// that have both the `highlight` and `shell` class
const urlElems = $('td.line.text')[0]
const urlText = $(urlElems).text()
let valor_dollar = Number(urlText.substring(4,7))
callback(valor_dollar);
});
}
and the route should be like this:
app.get('/',(req,res) => {
prova(function(value) {
res.body = { dollar_value: value };
res.end();
});
});

Dialogflow webhook fulfillment parameter is not working in post API

I make a code in webhook were i want invoke POST API and i want to invoke that api for that i have to pass some parameter but whenever i am trying to pass parameter coming from dialogflow its gives error. My code is like that
//Self Hosted Express Server
const bodyParser = require('body-parser')
var request = require('request-promise-native');
const { dialogflow } = require('actions-on-google');
const assistant = dialogflow({
clientId: "305xxxxxx7-rv9kocdq2xxxxouuq8f9ul2eg.apps.googleusercontent.com"
});
module.exports = (app) => {
const logger = console;
assistant.intent('Sales',(conv, params) => {
var pcode = params.myproduct;
// console.log(pcode)
const token = '3369708919812376';
const serviceID = '502';
const P_STATE_CD = 'ALL';
const P_FO_CD = 'ALL';
const P_DISTT_CD = 'ALL';
const P_DATE = '16/12/2019';
const P_PRD_GROUP = pcode;
const P_PERSONAL_NO = '106296';
var data = {"token" : token,"serviceID" : serviceID,"P_STATE_CD" : P_STATE_CD,"P_FO_CD" : P_FO_CD,"P_DISTT_CD" : P_DISTT_CD,"P_DATE" : P_DATE,"P_PRD_GROUP" : P_PRD_GROUP ,"P_PERSONAL_NO" : P_PERSONAL_NO };
var sdata = JSON.stringify(data);
const options = {
method: 'POST',
uri: 'http://chatbotWebservice/resources/webservice/service' ,
body: JSON.parse(sdata) ,
json: true
}
return request(options)
.then( body => {
var unit = body
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
unit.value2 = unit.saleInfo[0].QYTD
unit.value3 = unit.saleInfo[0].O_UOM
unit.value4 = null
unit.value5 = null
delete unit.saleInfo
var unit2 = JSON.stringify(unit)
console.log(unit2)
conv.ask(unit2);
})
.catch( err => {
console.error( err );
conv.ask('Something went wrong. What should I do now?');
});
})
And the error like this
TypeError: Cannot read property '0' of undefined
at request.then.body (/home/dbalounge/GoogleDF/service.js:40:44)
at process._tickCallback (internal/process/next_tick.js:68:7)
Please help me out this. Thank You in Advance
Apparently body is coming as a string, probably because the server is not setting the correct Content-Type to the response, and request is ignoring json: true option. So you should use JSON.parse on it, and then access the saleInfo
return request(options)
.then( body => {
var unit = JSON.parse(body)
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
/* ... */
});
Aside from that body: JSON.parse(sdata) in your call is not needed, you're stringifying data to sdata to parse it back again, just use data directly:
const options = {
method: 'POST',
uri: 'http://chatbotWebservice/resources/webservice/service' ,
body: data,
json: true
}

Request body is empty when posting form-data

I'm using a simple post request to my backend for a form data and for some reason the body is alwayes empty.
I'm trying to isolate this so i changed the content type to application json and changed the data to json and only this way i can send data.
Client side:
submitForm(event) {
event.preventDefault();
console.log("gggg");
const data = new FormData(event.target);
axios.post("http://localhost:4000/user-form-post",data).then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
Server side:
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
app.use(express.urlencoded());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
app.use(logger('dev'));
app.post('/user-form-post', (req,res) =>{
console.log("dfdf");
console.log(req.body); // alwayes print empty dict {}
res.end();
})
This is not working because it expects jsons(expected behavior):
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
Same behavior with Postman.
You will need to parse your form data from express side. For this you will have to use multer or multiparty. Try something like this. refer the documentation as well
const multiparty = require('multiparty');
app.post('/user-form-post', (req,res) =>{
let form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
Object.keys(fields).forEach(function(name) {
console.log('got field named ' + name);
});
});
})
when it comes to my issue,
i have this front end
const form = new FormData();
form.email = this.email;
form.password = this.password;
console.log("onSubmit -> form", form);
axios.post("http://localhost:3000/register", form )
onSubmit -> form FormData {email: "admin#gmail.com", password: "123"}
but the req.body in backend is empty, and i figured it out that the form in axios.post still need 1 more bracket {} even it's a object. like this
axios.post("http://localhost:3000/register", { form })
After that backend got body like this
req.body = { form: { email: 'admin#gmail.com', password: '123' } }
A problem with request body when you post data is data type .
I have recently a problem with Postman .
You should post data with type x-www-form-urlencoded or raw->JSON to fix the problem.
Goodluck.
You are using:
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
Please, also use given below line code but first install multer and write the code in top of your application:
var multer = require('multer');
var upload = multer();
app.use(express.json());
Faced the same issue , spent 2 days . Here are the solutions i found :
my request payload had JSON.stringify() , it will make body as {} empty object . when i removed JSON.stringify() and sent request it worked .
Content type should be multipart-form :boundary -----
Now if i externally set it to multipart-form , boundary thing was missing.
for few people it worked when you set content-type as false / undefined , boundary thing got added up,but not for me .
Even though i followed all steps and sending FormData as payload, payload was request payload object in network tab and was not FormData object , my request failed with 500 .
i tried the below code , its react + typescript (make necessary changes to avoid syntax errors)
import QueryString from 'qs';
import { ApiParams } from './xyzfile';
import { ApiHandlerRawType } from './types/xyzfile';
const setDefaultOptions = ({
method = '',
url = '',
params = {},
data = {},
signal = null,
headers = new Headers(),
...options
} = {}) => ({
method,
url,
params,
signal,
headers,
data,
...options
});
const setData = ({ method, data, ...options }: ApiHandlerRawType) => {
const option = options;
if (method !== 'GET' && option.isStreamData) {
option.body = data;
}
return {
method,
...option
};
};
const addRequestHeaders = ({ headers = new Headers(), ...options }) => {
const { existingHeaders }: ApiHandlerRawType = options;
if (existingHeaders) {
Object.entries(existingHeaders).forEach(([key, value]) => {
if (key !== 'Content-Type') headers.set(key, value);
});
}
return {
headers,
...options
};
};
export const ApiHandlerRaw = ({
url,
...originalOptions
}: ApiHandlerRawType): Promise<Response> => {
const options = setData(
addRequestHeaders(setDefaultOptions(originalOptions))
);
return fetch(url || '', options)
.then(response => {
if (!response.ok) throw new Error(response.statusText);
return Promise.resolve(response);
})
.catch(err => Promise.reject(err));
};
export const FileUploadApiHandler = async ({
headers,
...options
}: ApiHandlerRawType): Promise<Response | Blob> => {
const response = await ApiHandlerRaw({
headers,
isStreamData: true,
...options
});
return response;
};
export const fileApiService = ({
url,
method,
qsObject,
headers,
reqObjectAsStreamData
}: ApiParams): Promise<Response> => {
const qs = QueryString.stringify(qsObject, { addQueryPrefix: true });
const urlPath = `${url}${qs}`;
const data = reqObjectAsStreamData;
const existingHeaders = headers;
return FileUploadApiHandler({
url: urlPath,
method,
data,
existingHeaders
}) as Promise<Response>;
};
send the required variables from fileApiService . existingHeaders would be your app headers , eg : token / ids ... etc . data in fileApiService is the body .
I have also faced the same issue in the published code.
But I have fixed this issue by using the below code highlighted in the attached image :-
enter image description here
There is no use of "Content-Type" to fix this issue.
Hope you fix your issue by using the above code snippets.

Resources