Timeout error when calling external API from node.js - javascript

I have the following index.js(node v19.6.0) with POST request that calls external API and register a webhook. The url of the hook I am registering already works and tested it.
I googled the error but I couldn't find any results. The error comes when I call /register/hook method. It shows that there is a timeout but doesn't give me much more detail. Is the issue from the API provider or the way I am making REST calls ?
The code was generated by Alchemy.
const express = require('express');
const app = express();
const port = 8080;
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
app.post("/register/hook", (req, res) => {
const options = {
method: 'POST',
headers: {
accept: 'application/json',
'X-Alchemy-Token': 'abc...def',
'content-type': 'application/json'
},
body: JSON.stringify({
AddressWebhookParams: {addresses: ['0xe592427a0aece92de3edee1f18e0157c05861564']},
url: 'https://webhook.site/dfb04cab-8ca9-40f1-a522-66918d4a7015',
type: 'ADDRESS_ACTIVITY'
})
};
fetch('https://alchemy-sdk-core-example.com/create-webhook', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
})
Here is the error:
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:12789:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: ConnectTimeoutError: Connect Timeout Error
at onConnectTimeout (node:internal/deps/undici/undici:8236:28)
at node:internal/deps/undici/undici:8194:50
at Immediate._onImmediate (node:internal/deps/undici/undici:8225:13)
at process.processImmediate (node:internal/timers:475:21) {
code: 'UND_ERR_CONNECT_TIMEOUT'
}
}
[1]: https://docs.alchemy.com/reference/sdk-create-webhook

Double check url and parameters, everything else seems fine. It usually has timeout issue if url does not exist or maybe their servers are temporarily down.
Also I would recommend using axios. It would avoid you trouble like setting extra content headers.

If you don't send back a response from your handler, the client will wait indefinitely, or until it gives up:
fetch('https://alchemy-sdk-core-example.com/create-webhook', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err))
.finally(() => res.end()); // end the response process
More info here.

Related

Problem returning response from POST request with Axios in Node.js

I am writing a simple post request in a Firebase Cloud function, with Axios. This function calls an API endpoint and gets an object of profile details as response. My problem is to correctly return the response to the client.
In the code below, the Cloud Function logs the result correctly. But I can't figure out how to correctly return it to the client from the client-side callGetProfile() function. (Which runs inside a Vue3 method.)
I am probably missing something obvious but am very new to Node.js and HTPP requests.
Thanks for any help!
// MY FUNCTION IN NODE.JS (Firebase Cloud Functions)
exports.getProfile = functions.https.onCall((data, context) => {
var postData = {
profile_id: "xxxxxxxxxxxxxxxxx", //hardcoded here for testing but should be passed in "data" arg.
profile_type: "personal",
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'X-API-KEY': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX'
}
};
axios.post('xxxxxxxxxxxxxxxxxxxxxxxxxxxxx', postData, axiosConfig)
.then((res) => {
console.log(res.data) // this works, I get all the data correctly!!
return res // also tried res.data
})
.catch((err) => {
console.log("AXIOS ERROR: ", err);
})
});
// MY FUNCTION CLIENT SIDE (Vue3 method)
const functions = getFunctions();
const callGetProfile() = httpsCallable(functions, "getProfile");
callGetProfile()
.then((result) => {
console.log(result.data) // this doesn't work, data is "null"
})
.catch((e) => console.log(e));

SharePoint cannot get Access Token with JavaScript?

I need to get access token from SharePoint, In order to upload some files!
I got access token from postman successfully, But when I try to do the same request with Javascript!
const generateToken = async () => {
const headers = { "Content-Type": "application/x-www-form-urlencoded" };
var formdata = {};
formdata["grant_type"] = "client_credentials";
formdata["client_id"] = "<client_id>";
formdata["client_secret"] = "<client_secret>";
formdata["resource"] =
"00000003-0000-0ff1-ce00-000000000000/site_url#tenant_id";
const body = Object.keys(formdata)
.map((key) => `${key}=${formdata[key]}`)
.join("&");
var requestOptions = {
method: "POST",
headers,
body,
};
await fetch(
"https://accounts.accesscontrol.windows.net/<tenant_id>/tokens/OAuth/2",
requestOptions
)
.then((response) => response.json())
.then((result) => console.log(result))
.catch((error) => console.log("error", error));
};
generateToken();
when I execute the page which have this script I got this error
error TypeError: Failed to fetch
at generateToken
But IDK why the respose status is 200 OK, without returning body which contain access_token
Any help is much appreciated!
You cannot get the token from javascript like that, only from single page applications because of the security issues: you expose your client secret.
You can use Microsoft Authentication Library (MSAL) for JS instead.

Cloud Function returns undefiend when using POST

I am trying to send the param name in the Cloud Function managed by Firebase using POST method, I've read quite a few documentation, and no matter what I try it always returns undefined. Also is it safe to use this for sensitive data?
Client-Side
fetch(`https://<<APP-NAME>>.cloudfunctions.net/addCardForExistingCustomer`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
JSON.stringify(body: {'name': 'Name'})
})
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
Server-side (Firebase Cloud-Functions)
exports.addCardForExistingCustomer = functions.https.onRequest(async (request, response) => {
let name = await request.body.name
response.json({
response: `${name}`
})
})

function doesn't catch error from calling nodejs / express server endpoint

I have this route set up on my server with nodejs/express:
const testSync = (req, res) => {
//bookingLink and requestOptions defined here
fetch(bookingLink , requestOptions)
.then(response => response.text())
.then(result => {
res.sendStatus(200)
})
.catch(error => {
console.log('error', error)
res.sendStatus(404)
});
}
router.post('/test-sync', testSync);
And here is my client side call :
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({url: `${bookingLink}`})
}
fetch(`${protocol}//${domainName}/api/test-sync`, requestOptions)
.then(response => response.text())
.then(result => {
setSyncTest("success")
})
.catch(error => {
setSyncTest("fail")
});
The point of the client side call is to change the state of syncTest hook to "fail" if the link provided to the endpoint is bad. Now, if the link is bad it shows me the 404 error code in the console, but the syncTest doesn't change. I think it doesn't throw the error. How should I do to throw the error in order to change the hook state ?
Update your function to:
const testSync = (req, res) => {
//bookingLink and requestOptions defined here
return fetch(bookingLink , requestOptions)
.then(response => response.text())
.then(result => {
res.sendStatus(200)
})
.catch(error => {
console.log('error', error)
res.sendStatus(404)
});
}
router.post('/test-sync', testSync);
You need to return your fetch request, which is a Promise. What is happening now is that your function is returning right away before fetch is complete.
or more direct/cleaner layout:
const testSync = (req, res) => fetch(bookingLink , requestOptions)
.then(response => response.text())
.then(result => res.sendStatus(200))
.catch(error => {
console.log('error', error)
res.sendStatus(404)
});
router.post('/test-sync', testSync);
Client
Your client code is not handling the 404 correctly. Based on the instructions at MDN an HTTP 404 or even a 500 will not reject the promise. You will need to handle them in the then().
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.

catching error body using axios post

I am sending a status code 422 from my backend code with response body which contains the description of the error. I am using axios post as below to post a request:
post: function(url, reqBody) {
const request = axios({
baseURL: config.apiUrl,
url: url,
headers: {
'Content-Type': 'application/json',
'Authorization': sessionStorage.getItem('token')
},
method: 'POST',
data: reqBody,
responseType: 'json'
});
return request
.then((res) => {
return res;
})
.catch((error) => {
console.log(error);
return error;
})
}
The problem is when backend is returning error code 422, the error object I am catching has no information about response body. Is there any way I can retrieve the error text?
I had this same issue and the answer (as per Axios >= 0.13) is to specifically check error.response.data:
axios({
...
}).then((response) => {
....
}).catch((error) => {
if( error.response ){
console.log(error.response.data); // => the response payload
}
});
See here for more details.
The "body" of an AXIOS error response depends from the type of response the request had.
If you would like full details about this issue you can see this blogpost: How to catch the body of an error in AXIOS.
In summary AXIOS will return 3 different body depending from the error:
Wrong request, we have actually done something wrong in our request (missing argument, bad format), that is has not actually been sent. When this happen, we can access the information using error.message.
axios.get('wrongSetup')
.then((response) => {})
.catch((error) => {
console.log(error.message);
})
Bad Network request: This happen when the server we are trying to reach does not respond at all. This can either be due to the server being down, or the URL being wrong.
In this case, we can access the information of the request using error.request.
axios.get('network error')
.then((response) => {})
.catch((error) => {
console.log(error.request );
});
Error status: This is the most common of the request. This can happen with any request that returns with a status that is different than 200. It can be unauthorised, not found, internal error and more. When this error happen, we are able to grasp the information of the request by accessing the parameter specified in the snippets below. For the data (as asked above) we need to access the error.response.data.
axios.get('errorStatus')
.then((response) => {})
.catch((error) => {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
})
For those using await/async and Typescript
try {
const response = await axios.post(url, body)
} catch (error) {
console.log(error.response.data);
}
For react native it just worked for me
api.METHOD('endPonit', body)
.then(response => {
//...
})
.catch (error => {
const errorMessage = JSON.parse(error.request.response)
console.log(errorMessage.message)
})
We can check error.response.data as #JoeTidee said. But in cases response payload is blob type? You can get error response body with the below code.
axios({
...
}).then((response) => {
....
}).catch(async (error) => {
const response = error.response
if(typeof response.data.text === function){
console.log(await response.data.text()); // => the response payload
} else {
console.log(response.data)
}
});
I am returning a string from backend but expecting a json as response type. So I need to return an object instead of string for axios to process it properly.
In my case I wanted to retrieve a response 404 error message (body).
I got body with error.response.data but I couldn't display it because the type was ArrayBuffer.
Solution:
axios.get(url, { responseType: 'arraybuffer' }).then(
response => {...},
error => {
const decoder = new TextDecoder()
console.log(decoder.decode(error.response.data))
}
)
Related posts:
Converting between strings and ArrayBuffers

Categories