I'm working on my first CLI project and I'm having trouble getting it to execute API requests. I have tried fetch, axios, express, and a couple of npm packages, but I just can't figure out what's wrong. The project will console.log and gather user data from the command line, but will not retrieve API data. I'm using a fake API data url at this point just to be sure it works. Here is the code:
const axios = require('axios');
let apiResponse;
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(function(response) {
apiResponse = response;
console.log('Does this work?')
})
.catch(function (error) {
console.log(error, 'Error');
});
console.log('apiResponse: ', apiResponse);
In the command line I get 'apiResponse: undefined' when I run the file. Again, I've tried using several different libraries so I must be doing something fundamentally wrong. The console.log OUTSIDE of the function prints, but neither console.logs INSIDE are printing. Any help would be greatly appreciated!
I'm guessing in your console you see
undefined
Does this work?
The .get method is asynchronous, which means any assignment outside of then will most likely always be what you initialize it as, in this case nothing, or undefined.
Here's a high level of how things are actually happening:
1) Create undefined var apiResponse
2) axios.get(...)
3) console.log(apiResponse)
4) #2 completes, assigns to `apiResponse`
5) End execution
Here's one of many resources about Promises.
Move the log statement inside the .then() block.
const axios = require('axios');
let apiResponse;
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(function(response) {
apiResponse = response;
console.log('Does this work?')
console.log('apiResponse: ', apiResponse);
})
.catch(function (error) {
console.log(error, 'Error');
});
Related
External API responds with a multipart/mixed content type, and in my case, one part is some JSON and the second one is an octet-stream data of a PDF file. I'm building a POC with NodeJS 18 where fetch API is native and expect the response to have formData() method as per specs (https://developer.mozilla.org/en-US/docs/Web/API/Response/formData).
The sample code looks like this (apologies for mixing the promise and await):
fetch(url, {
method: 'GET',
headers,
}).then(async res => {
try {
const parts = await res.formData();
for(const entry of parts.entries()) {
writeFileSync(`./${entry[0]}`, entry[1]);
}
} catch (error) {
console.error('Error:', error);
}
process.exit();
})
.catch(error => {
console.error('Error:', error);
process.exit();
});
what I'm getting is a plain error
Error: TypeError: Response.formData: undefined
Which is quite unexpected. I couldn't find anything related to this behaviour and my first assumption is that the reason is the feature is still experimental.
Another reason I consider is that while API response has a proper Content-Type header it doesn't have Content-Length.
While I can parse the response body using some external dependency I'd rather stick with the native way. Any ideas about what the reason could be for this behavior?
I have a list of APIs I want to call GET simultaneously on all of them and return as soon as one API finishes the request with a response code of 200.
I tried using a for-loop and break, but that doesn't seem to work. It would always use the first API
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
let response;
for (const api of listOfApi) {
try {
response = await axios.get(api, {
data: {
url: 'https://example.com/',
},
});
break;
} catch (error) {
console.error(`Error occurred: ${error.message}`);
}
}
You can use Promise.race() to see which of an array of promises finishes first while running all the requests in parallel in flight at the same time:
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
Promise.any(listOfApi.map(api => {
return axios.get(api, {data: {url: 'https://example.com/'}}).then(response => {
// skip any responses without a status of 200
if (response.status !== 200) {
throw new Error(`Response status ${response.status}`, {cause: response});
}
return response;
});
})).then(result => {
// first result available here
console.log(result);
}).catch(err => {
console.log(err);
});
Note, this uses Promise.any() which finds the first promise that resolves successfully (skipping promises that reject). You can also use Promise.race() if you want the first promise that resolves or rejects.
I think jfriend00's answer is good, but I want to expand on it a bit and show how it would look with async/await, because that's what you are already using.
As mentioned, you can use Promise.any (or Promise.race). Both take an array of promises as argument. Promise.any will yield the result of the first promise that resolves successfully, while Promise.race will simply wait for the first promise that finishes (regardless of whether it was fulfilled or rejected) and yield its result.
To keep your code in the style of async/await as it originally was, you can map the array using an async callback function, which will effectively return a promise. This way, you don't have to "branch off into .then territory" and can keep the code more readable and easier to expand with conditions, etc.
This way, the code can look as follows:
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
try {
const firstResponse = await Promise.any(listOfApi.map(async api => {
const response = await axios.get(api, {
data: {
url: 'https://example.com/',
},
});
if (response.status !== 200) {
throw new Error(`Response status ${response.status}`, {cause: response});
}
return response;
}));
// DO SOMETHING WITH firstResponse HERE
} catch (error) {
console.error('Error occured:', error);
}
Side note: I changed your console.error slightly. Logging only error.message is a common mistake that hinders you from effective debugging later on, because it will lack a lot of important information because it prints only the message and not the error stack, the error name or any additional properties the error may have. Using .stack and not .message will already be better as it includes name and stack then, but what's best is to supply the error as separate argument to console.error so that inspect gets called on it and it can print the whole error object, with stack and any additional properties you may be interested in. This is very valuable when you encounter an error in production that is not so easy to reproduce.
I'm trying to make an api request and I do get the data because I tried console logging it and it shows on the console but I still get errors. Here are the errors:
TypeError: res.json is not a function
Error: Your `getServerSideProps` function did not return an object. Did you forget to add a `return`?
Here is my code:
export async function getServerSideProps(context) {
// API KEYS
const apiKeyLocationIQ = process.env.API_KEY_LOCATIONIQ;
const apiKeyWeather = process.env.API_KEY_WEATHER;
// API REQUEST OPTIONS
let cityLocationIQ = context.params.location;
let urlLocationIQ = "https://api.locationiq.com/v1/search.php?format=JSON&key="+apiKeyLocationIQ+"&q="+cityLocationIQ+"&limit=1";
// API REQUEST FOR LAT AND LON
try{
const res = await axios.get(urlLocationIQ).then((resLocation)=>{
return resLocation
})
const weather = await res.json();
return {
props: {
weather
}
}
}catch (error){
console.log( error);
}
}
UPDATE
I made some changes to the axios request and I get a different error. This time it says:
function cannot be serialized as JSON. Please only return JSON serializable data types.
here is how my updated axios request looks like:
try{
const res = await axios.get(urlLocationIQ, {responseType: 'json'})
const weather = await res;
return {
props: {
weather
}
}
}catch (error){
console.log( error);
throw error;
}
FINAL UPDATE
So it turns out that the axios request that I sent returns the entire request data including the headers etc. and the data that I needed was an object at the very bottom of the response, so all I had to do was to specify the data I needed.
Here is my code:
const location = await axios.get(urlLocationIQ).then((resLocation)=>{
return {resLocation.data[0]}
})
After that, the code works perfectly now.
To my knowledge, the value that axios.get() resolves to does not have a .json() method so thus the error you get.
You can tell Axios, ahead of time that you want it to parse a JSON response for you automatically.
const weather = (await axios.get(urlLocationIQ, {responseType: 'json'})).data;
But, that responseType is the default, so you can actually just do this assuming your response actually returns JSON:
const weather = (await axios.get(urlLocationIQ)).data;
I would assume you were perhaps expecting a .json() method like the fetch() interface uses, but that is not required with axios. By default axios reads the response body for you whereas the fetch() interface does not read the response body (other than the headers) until you specifically call a method such as .json(). But, that's not how axios works. In this regard, axios often requires less lines of code to make a request and get the response.
P.S. Note that the .then() here:
.then((resLocation)=>{
return resLocation
})
is pointless. The promise already resolves to that value so adding a .then() that just returns it again is of no use.
Also, note that getServerSideProps() is an async function. That means that it ALWAYS returns a promise and the resolved value of that promise will be whatever value you return from inside your function. The caller of that function needs to either use await or .then() to get the resolved value.
And, in your catch(), you need to rethrow the error so that the caller gets the error. Otherwise, you will just be eating the error and resolving the promise with undefined since there will be no return value.
} catch (error) {
console.log( error);
throw error;
}
I am currently working on a web app to manage an external database. I am not very familiar with express or NodeJS at this point so I wanted to ask how to send a JSON object to the client sides console without getting undefined?
I have this function to connect then select the what I need and afterwards I converted my JSON object to an array of JSON objects. It displays the data fine in the console as well.
async function connect() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (err) {
console.error('Unable to connect to the database:', error);
}
info = await sequelize.query('select * from LeadsInformation', { type: QueryTypes.SELECT });
const details = JSON.stringify(info);
console.log(details);
detailsArray = JSON.parse(details);
console.log(detailsArray);
}
Everything works fine in here, I can get the data and display it in the terminal.
This is my GET route:
app.get("/list", (req, res) => {
connect();
res.json(detailsArray)
});
I have tried a couple of suggested ways based on other explanations and code snippets but none of them has worked so far so I left it like that. I thought foreaching through the data itself in the request would be a solution but it did not work. I also tried using the JSON itself and trying to display it and also tried using the body parser library. Though the library has not been updated for two years. Also I am using axios to fetch the data. It works fine when I try sending a simple string like "hello world" for example.
Is there anything that I'm missing or do you have any other solutions? I would also appreciate an explanation as well if possible.
Edit: It might also have to do something with how I am getting the response in the frontend. I'll look into that as well and will update this thread if I sort it out!
This is the way I get the response. I am currently trying to show in the console. I am using axios API.
Axios({
method: "GET",
url: "http://localhost:5000/list",
headers: {
"Content-Type": "application/json"
}
}).then(res => {
console.log(res.data.json);
});
Probably you have undefined in route because connect function doesn't return anything.
Also connect is an async function it means that it returns Promise and you have to call .then method or use await to get value from it.
Here is the code snippet with fixes that I described above.
async function connect() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (err) {
console.error('Unable to connect to the database:', error);
}
info = await sequelize.query('select * from LeadsInformation', { type: QueryTypes.SELECT });
const details = JSON.stringify(info);
detailsArray = JSON.parse(details);
return detailsArray;
}
app.get("/list", async (req, res) => {
const result = await connect();
res.json(result)
});
Notice that in the router handler function I also use async and await because I call connect which is an asynchronous function.
The solution above did work and also another problem I had was that I wasn't getting the response correctly.
I ended up getting the response to the frontend after changing my code to the following from:
console.log(res.data.json);
To:
console.log(res.data[1]);
In zapier I use an action of Code By Zapier. It's based on node.js.
I need to use fetch for implementing REST-API of my CRM.
Here is the code I wrote, which runs well when I tried it with VS Code (outside Zapier):
// the code by zapier includes already the require('fetch')
var api_token = "..."; // my api
var deal_name = "Example"; // a string
fetch("https://api.pipedrive.com/v1/deals/find?term="+deal_name+"&api_token=" + api_token)
.then(function(res) {
return res.json();
}).then(function(json) {
var deal_id = json.data[0].id;
console.log("deal_id="+deal_id);
}).catch(function(error) {
console.log("error");
});
output = {id: 1, hello: "world"}; // must include output...
The error I got from Zapier is:
If you are doing async (with fetch library) you need to use a
callback!
Please help me with solving it.
This is a classic mistake when coding in Node.js/callback environments.
You are using console.log which prints to your console, but doesn't return data to the parent (Zapier in this case).
Here is an example of bad and good code:
// bad code
fetch(url)
.then(function(res) {
return res.json();
}).then(function(json) {
// when i run this in my node repl it works perfect!
// the problem is this doesn't return the data to zapier
// it just prints it to the system output
console.log(json);
});
// good code
fetch(url)
.then(function(res) {
return res.json();
}).then(function(json) {
// but if i swap this to callback, this works perfect in zapier
callback(null, json);
});
I hope this helps!
These days, you can also use async/await, as noted by the default comment at the top of the sample code block:
// this is wrapped in an `async` function
// you can use await throughout the function
const response = await fetch('http://worldclockapi.com/api/json/utc/now')
return await response.json()
See further examples in the docs: https://zapier.com/help/create/code-webhooks/javascript-code-examples-in-zaps#step-2
Note that the free-tier has a 1 second timeout (especially relevant if you use Promise.all() to execute multiple fetches!)