I have an await function() which waits for an external API. Usually, these APIs take 600ms to return data. But sometimes it's taking around 10 seconds. So how do I write a function so that if it takes more than 1 second, then break that function and resolve anyway (don’t wait for the API)
Here's the main function code which calls the API.
console.log("Before Await");
await fetchExternalAPI("data");
console.log("Continue if success or takes more than x seconds");
The actual function:
const fetch = require('node-fetch');
module.exports = async function fetchExternalAPI(data) {
try {
const response = await fetch("https://www.example.com/api/log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
const returnData = await response.json();
console.log("Response: " + JSON.stringify(returnData))
return returnData
}
catch (e) {
console.log("Error: " + e)
return e
}
}
I want to return success fo the above function
if fetch is success
or if exceeded more than x seconds
Both should return success no matter the real output.
You can use AbortController to abort the fetch method, inside a setTimeout with 1sec, and if the fetch resolved before the setTimeout callback execute, then clearTimeout
const fetch = require('node-fetch');
module.exports = async function fetchExternalAPI(data) {
try {
let controller = new AbortController();
let timeId = setTimeout(() => {
controller.abort()
}, 1000)
const response = await fetch("https://www.example.com/api/log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
signal: controller.signal
});
clearTimeout(timeId)
const returnData = await response.json();
console.log("Response: " + JSON.stringify(returnData))
return returnData
}
catch (e) {
console.log("Error: " + e)
return e
}
}
I think you need to use Axios.
npm i axios
than in body you have one property timeout.
const instance = await axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
You can add a promise in your call to resolve on time and reject if too long.
The following code has not been tested but that's the idea
const fetch = require('node-fetch');
module.exports = async function fetchExternalAPI(data) {
try {
const response = await fetch("https://www.example.com/api/log", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
await new Promise((resolve, reject) => {
setTimeout(reject, 50000));
resolve();
}).then(() => {
const returnData = await response.json();
console.log("Response: " + JSON.stringify(returnData))
return returnData
}).catch(() => {
return 'Took too long';
}));
}
catch (e) {
console.log("Error: " + e)
return e
}
}
I wrote the following code to translate from Arabic to English, I want the function to accept English as source and return the translated text,
can some one help me on this?
translateArtoEn(source) {
let url = `https://translation.googleapis.com/language/translate/v2?key=${API_KEY}`;
url += '&q=' + encodeURI(source);
url += `&source=ar`;
url += `&target=en`;
console.log(url)
fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
Accept: "application/json"
}
})
.then(res => res.json())
.then((response) => {
return response.data.translations[0]["translatedText"]
})
.catch(error => {
console.log("There was an error with the translation request: ", error);
});
}
The code you provided will not return any value since I assume the function is synchronous.
With this, you may use callback function to return the desired result after the parent function has already returned.
Please see below code for your reference:
const fetch = require('node-fetch');
function main(){
var source = '<your_desired_text_to_translate>';
translateArtoEn(source,myDisplayer);
}
function translateArtoEn(source,callback){
var API_KEY = '<your_API_key>';
let url = `https://translation.googleapis.com/language/translate/v2?key=${API_KEY}`;
url += '&q=' + encodeURI(source);
url += `&source=ar`;
url += `&target=en`;
fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
Accept: "application/json"
}
})
.then(res => res.json())
.then((response) => {
callback(response.data.translations[0].translatedText)
})
.catch(error => {
console.log("There was an error with the translation request: ", error);
});
}
function myDisplayer(result) {
// handle the result here
console.log("Translated text: ",result);
}
if (require.main === module) {
main();
}
In this code, callback is used to pass the returned value (after the execution of the parent function is finished) to another function that will handle the result.
I am getting the below result after my API call.
My node version is 12.x
{"type":"Buffer","data":[123,34,101,114,114,111,114,115,34,58,91,34,74,87,84,32,105,115,32,101,120,112,105,114,101,100,32,111,114,32,100,111,101,115,32,110,111,116,32,104,97,118,101,32,112,114,111,112,101,114,32,39,101,120,112,39,32,99,108,97,105,109,34,93,125,11]}
Please see the code snippet below:
let postOptions = {
host: 'vault.server',
path: '/v1/auth/gcp/login',
method: HTTPS.POST_REQUEST,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-Vault-Namespace': 'mynamespace'
},
json: true,
rpDefaults: {
strictSSL: false
}
};
let requestPayLoad = {
"role": this._vaultRole,
"jwt": signedJWT
};
console.log(JSON.stringify(requestPayLoad));
console.log(JSON.stringify(postOptions));
try {
let result = await HTTPS.makeRequest(postOptions, JSON.stringify(requestPayLoad), HTTPS.POST_REQUEST);
console.log('Response***************',JSON.stringify(result));
return result.auth.client_token;
}
Please see the below code snippet for the http make request method.
return new Promise((resolve, reject) => {
let rq = https.request(options, (res) => {
let response;
let chunks = [];
res.on('data', (chunk) => {
chunks.push(chunk);
});
res.on('end', () => {
response = Buffer.concat(chunks);
return resolve(response);
});
});
rq.on('error', (e) => {
return reject({'statusCode': 500, 'success': false, 'error': e.toString()});
});
if (type === 'POST') {
rq.write(data);
}
rq.end();
});
Please help me to resolve this
You are receiving the data as a Buffer. Use the toString() method to convert this buffer into a string inside the try block.
try {
let result = await HTTPS.makeRequest(postOptions, JSON.stringify(requestPayLoad), HTTPS.POST_REQUEST);
console.log('Response***************', result.toString());
return result.auth.client_token;
}
If you want to access the data from the response returned from you API call
do:
let data = result.data;
and I you want to get client_token as showing here:
return result.auth.client_token;
it's not possible because the response does not have auth attribute on it:
{"type":"Buffer","data":[123,34,101,114,114,111,114,115,34,58,91,34,74,87,84,32,105,115,32,101,120,112,105,114,101,100,32,111,114,32,100,111,101,115,32,110,111,116,32,104,97,118,101,32,112,114,111,112,101,114,32,39,101,120,112,39,32,99,108,97,105,109,34,93,125,11]}
If I call this function:
async function delete_get_note() {
let query = {
'command': 'delete_get_content',
'note_id': input_note_id.value
}
let response = await fetch(
'../api/api.php',
{
method: 'POST',
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: JSON.stringify(query)
}
);
let result = await response.json();
log.innerHTML = result.log;
}
log displays the content of the json returned by my server. However, if I try to encapsulate the call to fetch in a function:
async function json_exchange(object) {
let response = await fetch(
'../api/api.php',
{
method: 'POST',
headers: {'Content-Type': 'application/json;charset=utf-8'},
body: JSON.stringify(object)
});
return await response.json;
}
And call:
async function delete_get_note() {
let query = {
'command': 'delete_get_content',
'note_id': input_note_id.value
}
let result = await json_exchange(query);
log.innerHTML = result.log;
}
Then log displays undefined.
What did I get wrong when I tried to put the call to fetch in a function ?
You forgot to add the parentheses to the json call:
return await response.json;
-->
return await response.json()
Tip: You can use TypeScript checking to easily detect such errors:
i have already make 2 request.., but i want to make a patch request using the id from each response...
the other one will be put in the first one and the second one will be in the data
can we maybe pass it to the var ? idk how to do that..
for reference i use gatsbyjs and i use directusCMS
let url3 = `${process.env.GATSBY_DIRECTUS_API_URL}/gemaclc/items/pendaftar/:id (the id should be from the first response that we just made)?access_token=${process.env.GATSBY_DIRECTUS_TOKEN}`;
axios.patch(url3, data, {
data: JSON.stringify(
{
featured_image: 1 (id of the second response whcih is an image),
}),
})
event.preventDefault();
const data = new FormData()
data.append('file', this.state.selectedFile)
console.warn(this.state.selectedFile);
console.log(data);
// console.log("User Email :" + this.state.email)
// console.log("User nama :" + this.state.name)
// console.log("User telepon :" + this.state.telepon)
// console.log("User program :" + JSON.stringify([this.state.program]))
// console.log("User tanggal :" + this.state.tanggal_lahir)
// console.log("User tempat :" + this.state.tempat_lahir)
let url = `${process.env.GATSBY_DIRECTUS_API_URL}/gemaclc/items/pendaftar?access_token=${process.env.GATSBY_DIRECTUS_TOKEN}`;
let url2 = `${process.env.GATSBY_DIRECTUS_API_URL}/gemaclc/files?access_token=${process.env.GATSBY_DIRECTUS_TOKEN}`;
let url2 = `${process.env.GATSBY_DIRECTUS_API_URL}/gemaclc/files?access_token=${process.env.GATSBY_DIRECTUS_TOKEN}`;
axios(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
data: JSON.stringify({
status:"published",
nama: this.state.name,
// email: this.state.email,
// telepon: this.state.telepon,
// program: [1],
// tanggal_lahir: this.state.tanggal_lahir,
// tempat_lahir: this.state.tempat_lahir,
})
})
.then(res => {
console.log(res)
return axios.post(url2, data, {
data: JSON.stringify(
{
data: data,
}),
})
})
.then(res => {
console.log(res.data.data.id)
return axios.patch( url3, {
})
})
.catch(error => {
console.log(error)
});
I made a very simplified example of what you're trying to accomplish using async/await syntax since .then() would be messier to read; basically you can store the result of each post request in a variable to use in your patch request. I'm not sure what your response object looks like, so you may have to do some additional property extraction.
//simulates 1st post request
const post1= new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
});
//simulates 2nd post request
const post2= new Promise((resolve, reject) => {
setTimeout(() => resolve(2), 1000)
});
const groupedRequests = async() => {
//represents calling your 1st post request;
let id1 = await post1;
//represents calling your 2nd post request
let id2 = await post2;
//represents your patch request
console.log(id1, id2)
}
groupedRequests();
Edit: I went ahead and did the .then() version so you could see the comparison.
//simulates 1st post request
const post1= new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
});
//simulates 2nd post request
const post2= new Promise((resolve, reject) => {
setTimeout(() => resolve(2), 1000)
});
//stores first result
let id1;
//represents callings 1st post request
post1
.then(result => {
id1 = result;
//represents calling 2nd post request
return post2;
}).then(result => {
let id2 = result;
//represents your patch request
console.log(id1, id2)
})