Trying to make an API call to a remote endpoint with NestJS - node.js

var unirest = require("unirest");
var req = unirest("GET", "https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data");
req.query({
"ingr": "1 large apple"
});
req.headers({
"x-rapidapi-host": "HOST",
"x-rapidapi-key": "KEY",
"useQueryString": true
});
req.end(function (res) {
if (res.error) throw new Error(res.error);
console.log(res.body);
});
Im trying to make an API call with that doc and parameters in order to get a list of ingredients based on a search parameter.
This is my service:
async getAllNutrients(ingr: string) {
console.log(ingr);
const headersRequest = {
'x-rapidapi-host': 'edamam-edamam-nutrition-analysis.p.rapidapi.com',
'x-rapidapi-key': '5664b75c9fmsh66ac8e054422eb9p1600b8jsn878d097e8d2a',
useQueryString: true,
};
const result = await this.httpService.get(
`https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data` +
ingr,
{ headers: headersRequest },
);
console.log(result);
return result;
}
And this is my controller
#Get('/list?:ingr')
getMacros(#Query('ingr') ingr) {
return this.macroService.getAllNutrients(ingr);
}
I tried to change QUery and Param but none are working.
On postman i make an API call like this:
"localhost:3000/macros/list?ingr="1 large apple"
And my 2 console.log returns:
"1 large apple"
Observable { _isScalar: false, _subscribe: [Function] }
[Nest] 7460 - 2020-09-21 16:00:55 [ExceptionsHandler] Request failed with status code 404 +441782ms
Error: Request failed with status code 404
I tried to use pipe like this example:
getQuote(id){
return this.http.get('http://quotesondesign.com/wp-json/posts/' + id)
.pipe(
map(response => response.data)
);
}
But the result was the same. Any help?

Looks like your issue is in your controllers route. Changing #Get('/list?:ingr') to #Get('/list') should resolve this. I believe passing ?:ingr in the path is setting a param with key ingr.
Queries do not need to be added to the route. They are accessed using the #Query decorator.
Look at this for more info.

In your service function
const result = await this.httpService.get(
`https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data` +
ingr,
{ headers: headersRequest },
);
with ingr is 1 large apple then the API URL will become "https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data1 large apple".
I think this is an incorrect API URL, and you don’t want to call the API like that.
Change it to
`https://edamam-edamam-nutrition-analysis.p.rapidapi.com/api/nutrition-data?ingr=${ingr}`,

Related

How can I see the addres being sent via axios in nodejs?

I am trying to send parameters to a web API using axios in nodejs. This is the code snippet I am trying to use:
const app = require('axios');
app.get("https://api.somedomain.com/v1/", {
headers: {
Param1: 'value1',
Param2: 'value2',
Param3: 'value3'
}
})
.then((res) => {
console.log("Executing 'then' block"); // never executes
})
.catch((err) => {
if (err.response) {
console.log(`response.status: ${err.response.status}`);
} else if(err.request) {
console.log(`request.status: ${err.request.status}`);
}
})
When I execute the above I always get a response code 400 from the catch block.
So my question is this:
Can I display the string which is sent to the web API? Something that would look like this output:
https://api.somedomain.com/v1/?Param1=value1&Param2=value2&Param3=value3
Sorry if this seems naive but I have been able to make one get to retrieve a URL I will use to send the second get. The first get works and I have the URL. The second one always gives me the 400 response. (Or perhaps, is it because I have executed one get request on the app object and I have failed to clean up the app object to prepare for the next get request?)
Thanks in advance for your reply.
Peter
Change from headers to params in axios config:
const app = require('axios');
app.get("https://api.somedomain.com/v1/", {
params: { // HERE IS THE CHANGE!
Param1: 'value1',
Param2: 'value2',
Param3: 'value3'
}
})
.then((res) => {
console.log("Executing 'then' block"); // never executes
})
.catch((err) => {
if (err.response) {
console.log(`response.status: ${err.response.status}`);
} else if(err.request) {
console.log(`request.status: ${err.request.status}`);
}
})

There is a way to make Axios return the data as default response?

When we use Axios we always have to get the data from response. Like this:
const response = await Axios.get('/url')
const data = response.data
There is a way to make Axios return the data already? Like this:
const data = await Axios.get('/url')
We never used anything besides the data from the response.
You can use ES6 Destructing like this:
const { data } = await Axios.get('/url');
So you won't have write another line of code.
add a response interceptors
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data; // do like this
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
what i normally do is create a js file called interceptors.js
import axios from 'axios';
export function registerInterceptors() {
axios.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
);
}
in ./src/index.js
import { registerInterceptors } from './path/to/interceptors';
registerInterceptors();//this will register the interceptors.
For a best practice don't use axios every where, just in case in the future if you want to migrate to a different http provider then you have to change everywhere it uses.
create a wrapper around axios and use that wrapper in your app
for ex:
create a js file called http.js
const execute = ({url, method, params, data}) => {
return axios({
url,
method,//GET or POST
data,
params,
});
}
const get = (url, params) => {
return execute({
url, method: 'GET', params
})
}
const post = (url, data) => {
return execute({
url, method: 'POST', data
})
}
export default {
get,
post,
};
and use it like
import http from './http';
....
http.get('url', {a:1, b:2})
so now you can customize all over the app, even changing the http provider is so simple.

Cannot GET from Express.js PUT route, why?

Is there any reason you cannot execute a GET request from inside an Express.js router.put()?
I have two routes. The exact same api call works in a router.get() route and hangs in the router.put().
I've confirmed that
This works:
router.get('/:id', async (req, res) => {
const { headers } = req;
let result;
try {
result = await axios({ method:'get', url: '/some-url', headers });
} catch(error) {
res.status(500).send(new Error('myError');
}
res.send({ result });
});
This does NOT work:
router.put('/:id', async (req, res) => {
const { headers } = req;
let result;
let finalResult;
try {
result = await axios({ method:'get', url: '/some-url', headers });
} catch(error) {
res.status(500).send(new Error('myError');
}
// logic based on the result of the above GET determines what the value of `finalResult`
finalResult = { some: 'data' };
res.send({ finalResult });
});
Even though axios({ method:'get', url: '/some-url' }) is the exact same in both routes, it works in one and not the other.
The router.put() route always hangs for a long time and eventually Node outputs:
Error: socket hang up, code: 'ECONNRESET', etc...
Appreciate any help, I've spent over a day scratching my head over this.
No there's no such thing in express. Try hitting the GET request from postman or curl and see if response is coming. The root cause could be an invalid get request you're trying to make or that server on which you are making GET request could be down. You can run following to validate
app.put('/',async (req, res) => {
let response = await axios.get('https://google.co.in');
console.log(response.data);
res.send({"works": "hello"});
});
Root cause of my problem:
The http Headers had a key of Content-Length that prevented GET calls to resolve correctly.
Since these api calls occurred within a router.put() callback, the Headers had a Content-Length pointing to the size of the payload "body" that was being PUT to begin with.
Solution:
Remove that Content-Length field from my Headers when doing GETs inside router.put(), such that the GET has all the other Headers data except for Content-Length
Unfortunately, NodeJS just threw that Error: socket hang up message that was not very descriptive of the underlying problem.

How to call other REST APIs from your node micro-service and send the result as a response?

I am currently trying to implement a BFF (backend for front end architecture).
Using request-promise library I can successfully hit the other microservice but not able to return the result as a response from BFF microservice.
Each time it is returning this result Promise { pending } pending state, could somebody please help me out on this?
My main issue is to know how to receive data into BFF microservice from the other microservice that we are hitting and returning the result from microservice which is hitting other one.
Or if somebody could help me to let know how to access the result from inside .then of any promise?
The flow is like this:
client(ios/android)===(sends request)==>BFF Microservice==>BP microservice
(BFF Microservice handles the request and returns the response on the basis of result received from other microservice)
Microservice code which is calling another microservice:
import yagmodel from '../../lib/models/yag-model'
import {errorsList} from '../../lib/errors/errorsList'
import request from 'request-promise'
import config from 'config'
//template below to call the REST APIs of other microservices.
export async function getAllBP (req,res) {
let yagresponse// this varaible is defined to get data from inside(rs.then )
const username= req.swagger.params.username.value
const authheader= req.swagger.params.Authorization.value
console.log("Authorization:"+authheader)
let rs= await yagmodel.bp(username,authheader)
console.log(rs)
rs.then((response)=>{
// console.log(response.body)
yagresponse=response.body
//console.log(rsp)
}).catch((err)=>{
console.log(err)
console.log('errorstatuscode:'+err.statusCode)
})
res.status(200).send(yagresponse)
}
yag-model.js code:
import {errorsList} from '../../lib/errors/errorsList'
import request from 'request-promise'
module.exports.bp = async function getBP(username,authheader){
const options={
uri: `http://localhost:4000/Health/BP/`+username,
json: true,
resolveWithFullResponse: true,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Accept': 'application/json; charset=utf-8',
'Authorization':authheader
},
method: 'GET'
}
return request(options).then ((response)=>{
return response.body
}).catch((err)=>{
console.log(err)
console.log('errorstatuscode:'+err.statusCode)
})
}
I think you mix to match await oprators with promises, when you can use only await.
I create simplefied version of your code:
yag-model.js
const request = require('request-promise');
module.exports.bp = async function getBP () {
const options = {
uri: `https://api.postcodes.io/random/postcodes`,
json: true,
resolveWithFullResponse: true,
method: 'GET'
};
return request(options).then((response) => {
return response.body
}).catch((err) => {
console.log(err);
console.log('errorstatuscode:' + err.statusCode)
})
};
and usgae in sample bf.js
const yagmodel = require('./yag-model');
async function getAll(){
const result = await yagmodel.bp();
console.log(result);
};
getAll();
And the result is the response on my console.
F:\Projekty\Learn\lear-node>node bf
{ status: 200,
result:
{ postcode: 'BH23 5DA',
quality: 1,
eastings: 420912,
I recommend to look on this great resource about asunc functions http://exploringjs.com/es2016-es2017/ch_async-functions.html from Dr. Axel Rauschmayer
Please don't confuse between promises returned from request-promise and async functions.
async functions can be awaited to get a resolved promise and make your code look good.
I believe in letting people resolve their own problems and just guiding them along the way so just to make sure, aren't you getting the return from the resolved promise in this line of yours:
console.log(rs)
Moreover, by looking at your snippet, you're returning a response.body from within request-promise's thenable. You cannot catch any response errors from the response body, right?
I'd strongly suggest following a pattern wherein you'd catch errors (where you should) and display proper messages when you do. Wrapping your await call in a try/catch can help catch uncaught errors from request-promise.
Peace!
You have two promise, then you can use two await to get resolve them.
export async function getAllBP (req,res) {
let yagresponse// this varaible is defined to get data from inside(rs.then )
const username= req.swagger.params.username.value
const authheader= req.swagger.params.Authorization.value
console.log("Authorization:"+authheader)
let rs= await yagmodel.bp(username,authheader)
console.log(rs)
let response= await rs()
res.status(200).send(response);
}

Render raw image bytes to response body

I'm creating an API that creates authorized API calls to Google's APIs, specifically Drive for this question. My API is working fine and uses Google's Node API to make the requests. When I fire off a request to this resource, I get back the following response:
{
"kind": "drive#file",
"id": "...",
"name": "bookmobile.jpg",
"mimeType": "image/jpeg"
}
I use the above response to determine the MIME type of the file I'm to display later. I then make a subsequent call to the same endpoint, but specifying alt=media as an option to download the file as specified in Google's Guide. If I console.log or res.send() the response, I get the following output:
Which we can see is the raw image bytes from the API call. How do I render these bytes to the response body properly? My code is as follows:
// DriveController.show
exports.show = async ({ query, params }, res) => {
if (query.alt && query.alt.toLowerCase().trim() === 'media') {
// Set to JSON as we need to get the content type of the resource
query.alt = 'json'
// Get the Files Resource object
const options = createOptions(query, params.fileId)
const filesResource = await Promise.fromCallback(cb => files.get(options, cb))
// Grab the raw image bytes
query.alt = 'media'
await createAPIRequest(createOptions(query, params.fileId), 'get', res, filesResource)
} else {
await createAPIRequest(createOptions(query, params.fileId), 'get', res)
}
}
async function createAPIRequest (options, method, res, filesResource = {}) {
try {
const response = await Promise.fromCallback(cb => files[method](options, cb))
if (filesResource.hasOwnProperty('mimeType')) {
// Render file resource to body here
} else {
res.json(response)
}
} catch (error) {
res.json(error)
}
}
Searching through various answers here all seem to point to the following:
res.type(filesResource.mimeType)
const image = Buffer.from(response, 'binary')
fs.createReadStream(image).pipe(res)
But this kills my Express app with the following error:
Error: Path must be a string without null bytes
How would I go about rendering those raw image bytes to the response body properly?
The Google API client returns binary data as a string by default, which will corrupt image data when it happens. (The issue is discussed on this thread: https://github.com/google/google-api-nodejs-client/issues/618). To fix, use the encoding: null option when requesting the file contents:
files[method](options, { encoding: null }, cb))

Resources