Pokemon API fetch (axios) data then render all the data issue - node.js

I'm fairly new to front end and back end dev. Trying to build a small web with React / Node.js, trying to fetch data from Pokemon API: https://pokeapi.co/
I'm trying to fetch (using axios) all pokemons' ID, Name, Types, Pics then render it. I got all the data back, but when rendering it, it can only render the first data. With error message
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
.....
code:
router.get('/', async function (req, res, next) {
const pokemonUrl = 'https://pokeapi.co/api/v2/pokemon?limit=10';
var pokemonPic = [];
var pokemons = [];
// a nest fetch (aixos) data
await axios({
method: 'get',
url: pokemonUrl,
timeout: 10000
})
.then((all) => {
// store data and give access for all
let { data } = all;
// fetch pokemon infomation from each url
data.results.forEach(function (pokemon) {
// the second url to fecth data
const urll = pokemon.url;
axios({
method: 'get',
url: urll,
timeout: 10000
})
.then((respon) => {
// otherwise have no access to respon
let { data } = respon;
var pokeTypes = [];
let pokeName = data.name;
let pokeID = data.id;
let pokePic = data.sprites.front_default;
// pokemon types
for (var i = 0; i < data.types.length; i++) {
pokeTypes += data.types[i].type.name + ' '
}
// check the outcome
console.log(pokeID, pokeName, pokeTypes, pokePic);
// render index from view
res.render('index', {
pokePic,
name: pokeName,
pokeTypes: pokeTypes,
pokeID
})
// return pokeID, pokeName, pokeTypes, pokePic;
})
.catch((error) => {
console.log(error);
})
})
})
Any hint will be helpful.
Warm thanks.

You can access the image of a pokemon by using :
const getPokemon = async (pokemonName) => {
await axios.get(`https://pokeapi.co/api/v2/pokemon/${pokemonName}`).then(response => {
return response.data.sprites.front_default
}).catch(err => console.log(err))
}
getPokemon('pikachu')
You should get this :

Related

Azure Machine Learning REST Endpoint - Failed to Fetch

I created an Azure Machine Learning model with a REST Endpoint as a way to consume it. When I run the service using Postman everything seems to work fine.
However, when I try to create an HTML website (Codepen) with a javascript to call the REST Endpoint I only get an Error: Failed to Fetch message.
I also tried with Azure Static Web Apps and I am unsuccessful as well.
I was however able to verify (in the Console) that my input to the Rest Endpoint via Codepen is the same as Postman.
Is there anything I am missing out here?
Here is a sample of my javascript:
<script>
const form = document.querySelector('#agriculture-form');
form.addEventListener('submit', (event) => {
event.preventDefault();
const areaHarvest = parseFloat(document.querySelector('#area-harvest').value);
const farmGatePrice = parseFloat(document.querySelector('#farm-gate-price').value);
const volumeOfImport = parseFloat(document.querySelector('#volume-of-import').value);
const lowTemp = parseFloat(document.querySelector('#low-temp').value);
const averageTemp = parseFloat(document.querySelector('#average-temp').value);
const highTemp = parseFloat(document.querySelector('#high-temp').value);
const precipitationMm = parseFloat(document.querySelector('#precipitation-mm').value);
const precipitationDays = parseFloat(document.querySelector('#precipitation-days').value);
const tropicalCyclones = parseFloat(document.querySelector('#tropical-cyclones').value);
const volumeProductionGuess = 0;
const data = {
"Area_Harvested": areaHarvest,
"FarmGatePricePHPPSA": farmGatePrice,
"Volume_of_Import": volumeOfImport,
"temp_low": lowTemp,
"temp_ave": averageTemp,
"temp_high": highTemp,
"precipitation_mm": precipitationMm,
"precipitation_days": precipitationDays,
"tropical_cyclone": tropicalCyclones,
"Volume_of_Production": volumeProductionGuess
};
const formattedData = [data];
console.log('formatted data:', formattedData);
const testData = JSON.stringify(formattedData);
console.log('test data:', testData);
document.getElementById("demo").innerHTML = testData;
fetch('http://ziggyapimanagementservice.azure-api.net/score', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': 'cd529cc993494fdfb1530eaf04ae63dc'
},
body: testData
})
.then(response => response.json())
.then(data => {
console.log(data);
const result = data.result[0]; // Get the result array from the response
const volumeForecastElement = document.querySelector('#volume-forecast');
volumeForecastElement.textContent = result.join(', '); // Update the text content of the <b> element with the result array joined by commas
document.getElementById("result").innerHTML = result;
})
.catch(error => {
document.getElementById("error").innerHTML = error.message;
console.error(error.message)
});
});
And here is what I get in Postman:

Using nodejs to make https request to multiple servers

I am trying to make a site for crypto data using coin-gecko's API.
They have 2 different end points for what i require and as such require 2 different URLs.
I had no problem using into the globalUrl to get data such as the total Market cap, volume, etc. which i was able to render into my ejs.
My problem is now i cannot use the other URL for this, seeing as I cannot make another get request, what would be the best way to get data from the topCoinsUrl such as say the "id" of bitcoin from the 2nd url please
const https = require('https');
const app = express();
app.get("/", function(req, res) {
const globalUrl = "https://api.coingecko.com/api/v3/global";
const topCoinsUrl = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false&price_change_percentage=1h"
https.get(globalUrl , function(response) {
let data = "";
response.on("data", function(chunk) {
data += chunk
});
response.on("end", function() {
const globalMarket = JSON.parse(data);
const totalCryptocurrencies = globalMarket.data.active_cryptocurrencies
let totalMarketCap = globalMarket.data.total_market_cap.usd
let totalMarketCapUsd = totalMarketCap.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
});
let totalVolume = globalMarket.data.total_volume.usd
let total24hVolume = totalVolume.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
});
let markets = globalMarket.data.markets
let bitcoinMarketShare = Math.round(globalMarket.data.market_cap_percentage.btc);
res.render("home", {
totalCryptocurrencies: totalCryptocurrencies,
totalMarketCap: totalMarketCapUsd,
total24hVolume: total24hVolume,
markets: markets,
bitcoinMarketShare: bitcoinMarketShare
});
})
}).on("error", function(error) {
console.error(error)
});
});
// Ideally i would like to add this to get the ID of bitcoin, but I get an error when i try to use the 2 get requests:
https.get(topCoinsUrl, function(response) {
let data = "";
response.on("data", function(chunk) {
data += chunk
});
response.on("end", function() {
const topCoinsUrl = JSON.parse(data);
let bitcoinId = topCoinsUrl[0].symbol
res.render("home", {
bitcoinId: bitcoinId
})
})
// Error handler
}).on("error", function(error) {
console.error(error)
});
});
If you wish to make 2 simultaneous requests, you should use something like Promise.all() . Create two network requests and fire them at the same time using Promise.all & collect their result.
You can use Blurebird as well... http://bluebirdjs.com/docs/api/promise.all.html

How to add data via post in a request in Puppeteer?

I am trying to scrape a webpage with Puppeteer. Enter, navigate through some pages and in the data pages (those that are paginated) add POST data (emulating the form).
The event to intercept the request can only be created once, so all calls will be affected by the data sent via POST. (Node Puppeteer, page.on( "request" ) throw a "Request is already handled!")
I didn't find much information on this (how do POST request in puppeteer?), and finally did the following:
Create a function that will always be called (on each request).
Query an attribute of the function to see if it has an object.
If you have it, embed the data via POST; and remove the attribute.
If the attribute does not exist, continue without embedding data.
const openConnection = async () => {
const browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox"],
});
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on("request", requestPost);
return { browser, page };
};
const requestPost = async (req) => {
if (typeof requestPost.data === "object") {
requestPost.data.headers = { ...req.headers(), ...requestPost.data.headers };
await req.continue(requestPost.data);
delete requestPost.data;
} else {
await req.continue();
}
};
const getData = async (m, y, p, l) => {
const { browser, page } = await openConnection();
let data = [];
let pagina = p;
do {
/* JUST because this attribute is being created, the next request that is created in the page.goto() that follows, will be altered with these attributes */
requestPost.data = {
method: "POST",
postData: `&pagina=${pagina}&mes=${m}&year=${y}`,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
};
await page.goto("https://url.com/info.cgi", { waitUntil: "networkidle2" });
// Now I work the data and add it to the end
// data = data.push();
pagina++;
} while (pagina < p + l);
await closeConnection(page, browser);
return data;
};

Getting data from external API before render

I have an application based on the React Starter Kit.
Every page have a fetch function that getting data from API in componentDidMount lifecycle.
I want to get data first and then render page with data and return it to the client. UX in my case no matter.
I know that RSK is isomorphic, I'm ready to change boilerplate or create my own. But I do not understand how to fetch data from API before render page(I mean how to tell express server what data requires).
How App fetching data now:
example_page.js:
import getBooks from 'queries/getAllBooks';
...
class IdTag extends React.Component {
componentDidMount(){
this.getBooks();
}
getBooks() => {
const request = getBooks();
request
.then(...)
}
}
getAllBooks.js:
import doGet from './doGet';
let result = '';
const request = async () => {
const reqUrl = '/api/books/';
result = await doGet(reqUrl);
return result;
};
export default request;
doGet.js:
const request = async reqUrl => {
let requestResult = null;
const doQuery = async () => {
const response = await fetch(reqUrl, {
method: 'GET',
});
const result = await response.json();
result.status = response.status;
return result;
};
requestResult = await doQuery();
return requestResult
}
...
export default request;
server.js:
...
app.get('/api/*', async (req, res) => {
const newUrl = config.gate.URL + req.url.replace('/api', '');
const accessToken = req.cookies.access_token;
const response = await nodeFetch(newUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const result = await response.json();
res.status(response.status);
res.json(result);
});
...
If each page has api calls, then Its better to use redux and redux saga. Purpose of redux saga is to handle api calls. It will process the actions in a Q. The moment u call api using fetch method, create below actioncreators
1) InitialLoading(true)
2) Fetch api call action creator
3) Based on success, error create action creator to store fetch method output data in store
4) InitialLoading(false)
You could simply set a flag when you begin your fetch, and while it's fetching return null instead of rendering. Something like:
flag = true;
request = getBooks();
request.then(flag = false);
and then:
render(){
if (flag){
return null;
} else {
return this.view;
}
}

How do I get data out of a Node http(s) request?

How do I get the data from a https request outside of its scope?
Update
I've seen Where is body in a nodejs http.get response?, but it doesn't answer this question. In fact, that question isn't answered accurately, either. In the accepted answer (posted by the asker), a third party library is used. Since the library returns an object different from that returned by http.get() it doesn't answer the question.
I tried to set a variable to the return value of http.get() using await, but that returns a http.clientRequest and doesn't give me access to the response data that I need.
I'm using Node v8.9.4 with Express and the https module to request data from Google's Custom Search.
I have two routes. One for a GET request and one for a POST request used when submitting a form on the front page. They both basically serve the same purpose... request the data from CSE and present the data as a simple JSON string. Rather than repeat myself, I want to put my code for the CSE request into a function and just call the function within the callback for either route.
I thought about returning all the way up from the innermost callback, but that won't work because it wouldn't get to the request's error event handler or the necessary .end() call.
Here's a subset of the actual code:
app.get('/api/imagesearch/:query', newQuery)
app.post('/', newQuery)
function newQuery (req, res) {
let query = req.body.query || req.params.query
console.log(`Search Query: ${query}`)
res.status(200)
res.set('Content-Type', 'application/json')
// This doesn't work
let searchResults = JSON.stringify(cseSearch(req))
res.end(searchResults)
}
function cseSearch (request) {
let cseParams = '' +
`?q=${request.params.query}` +
`&cx=${process.env.CSE_ID}` +
`&key=${process.env.API_KEY}` +
'&num=10' +
'&safe=high' +
'&searchType=image' +
`&start=${request.query.offset || 1}`
let options = {
hostname: 'www.googleapis.com',
path: '/customsearch/v1' + encodeURI(cseParams)
}
let cseRequest = https.request(options, cseResponse => {
let jsonString = ''
let searchResults = []
cseResponse.on('data', data => {
jsonString += data
})
cseResponse.on('end', () => {
let cseResult = JSON.parse(jsonString)
let items = cseResult.items
items.map(item => {
let resultItem = {
url: item.link,
snippet: item.title,
thumbnail: item.image.thumbnailLink,
context: item.image.contextLink
}
searchResults.push(resultItem)
})
// This doesn't work... wrong scope, two callbacks deep
return searchResults
})
})
cseRequest.on('error', e => {
console.log(e)
})
cseRequest.end()
}
If you're curious, it's for a freeCodeCamp project: Image Search Abstraction Layer
using promise method solve this issue.
cseSearch(req).then(searchResults=>{
res.end(searchResults)
}).catch(err=>{
res.status(500).end(searchResults)
})
function cseSearch (request) {
return new Promise((resolve, reject)=>{
...your http request code
cseResponse.on('end', () => {
let cseResult = JSON.parse(jsonString)
let items = cseResult.items
items.map(item => {
let resultItem = {
url: item.link,
snippet: item.title,
thumbnail: item.image.thumbnailLink,
context: item.image.contextLink
}
searchResults.push(resultItem)
})
resolve(searchResults);
})
})
}
Based on what I explained in the comments, to give you an idea how compact your code could be using the request-promise library, here's what you could use:
const rp = require('request-promise-native');
app.get('/api/imagesearch/:query', newQuery)
app.post('/', newQuery)
function newQuery (req, res) {
let query = req.body.query || req.params.query
console.log(`Search Query: ${query}`)
cseSearch(req).then(results => {
res.json(results);
}).catch(err => {
console.log("newQueryError ", err);
res.sendStatus(500);
});
}
function cseSearch (request) {
let cseParams = '' +
`?q=${request.params.query}` +
`&cx=${process.env.CSE_ID}` +
`&key=${process.env.API_KEY}` +
'&num=10' +
'&safe=high' +
'&searchType=image' +
`&start=${request.query.offset || 1}`
let options = {
hostname: 'www.googleapis.com',
path: '/customsearch/v1' + encodeURI(cseParams),
json: true
};
return rp(options).then(data => {
return data.items.map(item => {
return {
url: item.link,
snippet: item.title,
thumbnail: item.image.thumbnailLink,
context: item.image.contextLink
};
});
});

Resources