I am trying to fetch data from the Storm Glass API. I am using their template Fetch request (https://docs.stormglass.io/?javascript#point-request).
When I run the script the console reads out "Promise { <pending> }" indefinitely. So, the request is not returning a value but I can't work out why. Any ideas?
I have replaced my API key with <My API key>
const http = require('http')
const fetch = require('isomorphic-fetch');
http.createServer((req, res) => {
////////////////////////////////////////////////App code
const lat = 58.7984;
const lng = 17.8081;
const params = 'waveHeight,airTemperature';
fetch(`https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`, {
headers: {
'Authorization': '<My API key>'
}
}).then(function(response) {
// Do something with response data.
const jsonData = response.json();
console.log(jsonData)
});
/////////////////////////////////////////////////////////
}).listen(3000);
console.log("service running on http://localhost:3000");
The response.json function return a Promise, not the deserialized object. Your code should read:
fetch(`https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`, {
headers: {
'Authorization': '<My API key>'
}
})
.then(response => response.json())
.then(function(jsonData) {
// Do something with response data
console.log(jsonData)
});
As an aside to gretro's answer, you may have got the idea that const json = response.json() would work from looking at async/await code as it's very similar, so here's how that code might look if written that way. It's traditionally wrapped in a try/catch, so I've included that too.
http.createServer(async (req, res) => {
const lat = 58.7984;
const lng = 17.8081;
const params = 'waveHeight,airTemperature';
try {
const endpoint = `https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`;
const params = { headers: { 'Authorization': '<My API key>' } };
const response = await fetch(endpoint, params);
const jsonData = await response.json();
console.log(jsonData);
} catch (err) {
console.error(err);
}
}).listen(3000);
You can resolve promise by using async/awiat.
fetch(`https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`, {
headers: {
'Authorization': '<My API key>'
}
})
.then(async response => {
const jsonData = await response.json();
console.log(jsonData)
})
Related
I've created a function to get the current user's data from the Reddit API, as part of a Reddit object with multiple functions.
async getCurrentUserId() {
if (userID) return userID;
const token = await Reddit.getAccessToken().then(val => {
return val;
})
const url = "https://oauth.reddit.com/api/v1/me"
const headers = {
"Authorization": `Bearer ${token}`,
"User-Agent": "blablabla",
};
const response = await fetch(url, { headers: headers });
if (response.ok) {
const jsonResponse = await response.json();
return jsonResponse.name;
}
},
However, when I try and extract the data, I keep getting a promise rather than the resolved value, and I can't seem to be able to figure it out.
const userID = Reddit.getCurrentUserId().then(val => {
return val;
}) // returns "PromiseĀ {<pending>}"
Assistance with this would be appreciated.
You either need to do your logic inside .then(), or simplify by using await:
const token = await Reddit.getAccessToken();
...
const userID = await Reddit.getCurrentUserId();
I've found this function to generate oauth-1.0a header:
// auth.js
const crypto = require("crypto");
const OAuth1a = require("oauth-1.0a");
function auth(request) {
const oauth = new OAuth1a({
consumer: {
key: process.env.TWITTER_API_KEY,
secret: process.env.TWITTER_API_SECRET_KEY,
},
signature_method: "HMAC-SHA1",
hash_function(baseString, key) {
return crypto.createHmac("sha1", key).update(baseString).digest("base64");
},
});
const authorization = oauth.authorize(request, {
key: process.env.TWITTER_ACCESS_TOKEN,
secret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
});
return oauth.toHeader(authorization).Authorization;
}
module.exports = auth;
It works fine if I try it with Twitter API v1.1:
// v1.js
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const url = "https://api.twitter.com/1.1/favorites/create.json";
const method = "POST";
const params = new URLSearchParams({
id: "1397568983931392004",
});
axios
.post(url, undefined, {
params,
headers: {
authorization: auth({
method,
url: `${url}?${params}`,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
But if I try it with Twitter API v2:
// v2.js
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const url = `https://api.twitter.com/2/users/${process.env.TWITTER_USER_ID}/likes`;
const method = "POST";
const data = {
tweet_id: "1397568983931392004",
};
axios
.post(url, data, {
headers: {
authorization: auth({
method,
url,
data,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
it fails with:
{
title: 'Unauthorized',
type: 'about:blank',
status: 401,
detail: 'Unauthorized'
}
I tried encoding the body of the request as suggested here, but get the same error:
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const querystring = require("querystring");
const url = `https://api.twitter.com/2/users/${process.env.TWITTER_USER_ID}/likes`;
const method = "POST";
const data = percentEncode(
querystring.stringify({
tweet_id: "1397568983931392004",
})
);
function percentEncode(string) {
return string
.replace(/!/g, "%21")
.replace(/\*/g, "%2A")
.replace(/'/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29");
}
axios
.post(url, data, {
headers: {
"content-type": "application/json",
authorization: auth({
method,
url,
data,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
If tested with Postman, both endpoints (1.1 and 2) work fine with the same credentials.
Any ideas on what am I doing wrong while using v2 or how to get it working with Twitter API v2?
I suspect it's something related with the body of the request as that's the main diference between both requests, but haven't been able to make it work.
Figure it out, the body of the request should not be included while generating the authorization header:
require("dotenv").config();
const axios = require("axios");
const auth = require("./auth");
const url = `https://api.twitter.com/2/users/${process.env.TWITTER_USER_ID}/likes`;
const method = "POST";
const data = {
tweet_id: "1397568983931392004",
};
axios
.post(url, data, {
headers: {
authorization: auth({
method,
url,
}),
},
})
.then((data) => {
return console.log(data);
})
.catch((err) => {
if (err.response) {
return console.log(err.response);
}
console.log(err);
});
Basically, when making a post request to Twitter API v1.1, the data should be encoded, should be used to generate the authorization header, and the post request should be sent as application/x-www-form-urlencoded.
When making a post request to Twitter API v2, the data should not be encoded, should not be included while generating the authorization header, and must be sent as application/json.
Hope this becomes helpful to someone else.
Currently I am trying to convert a working fetch POST request into an Axios POST request. However, I keep getting this Error -> 'Uncaught (in promise) Error: Request failed with status code 400'.
This is my working fetch request code below:
async function postData(url ='', data = { }) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json' },
body: JSON.stringify(data) });
let txoData = await response.json();
let a = txoData.payload.substring(68,134);
console.log(a);
console.log(txoData)
}
postData('https://merchantapi.taal.com/mapi/tx', { 'rawtx': rawtx })
.then(txoData => { return txoData })
.then(a => { return a })
and this is my NOT working convert to Axios attempt below:
async function postData() {
const response = await axios.post('https://merchantapi.taal.com/mapi/tx', {'rawtx': rawtx});
let txoData = await response.json();
let a = txoData.payload.substring(68,134);
console.log(a);
console.log(txoData)
}
postData()
.then(txoData => { return txoData })
.then(a => { return a })
I would greatly appreciate any help on this issue :)) x
To pass custom headers while using axios supply an object containing the headers as the last argument
presently I am attempting to make 2 different api calls one after the other within 1 java/nodejs script. It seems after my first function runs successfully, the second one errors with the following:
FetchError: request to failed, reason: socket hang up;
type: 'system',
errno: 'ECONNRESET',
code: 'ECONNRESET'
Below is a code snippet of what I have tried thus far:
const fetch = require("node-fetch");
const formData = require("form-data");
const fs = require("fs");
//const express = require("express");
var apiName = "<LOCAL SYSTEM FILE TO UPLOAD>";
var lookupName = "<LOCAL SYSTEM FILE TO UPLOAD>";
var accessToken = "Bearer <ACCESS TOKEN>";
var url = '<URL API #1>';
var url2 = '<URL API #2>;
var headers = {
'Accept': 'application/json',
'Authorization': accessToken,
};
const form = new formData();
const buffer2 = fs.readFileSync(lookupName);
const buffer = fs.readFileSync(apiName);
const uploadAPI = function uploadAPI() {
form.append("Content-Type", "application/octect-stream");
form.append('file', buffer);
fetch(url, {method: 'POST', headers: headers, body: form})
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
});
};
const uploadLookup = function uploadLookup() {
form.append("Content-Type", "application/octect-stream");
form.append('file', buffer2);
fetch(url2, {method: 'PUT', headers: headers, body: form})
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
});
};
if (!apiName !== true) {
uploadAPI()
} else {}
if (!lookupName !== true) {
console.log("Uploading Lookup File");
uploadLookup()
} else {}
I tried using a "setTimeout" function which does not seem to work as I would have liked it to. My best guess is each API call would need to be it's own separate socket connection? Any help with getting me in the right direction is appreciated.
Promise.all([
fetch(url),
fetch(url2)
]).then(function (res) {
// Get a JSON object from each of the responses
return res.json();
}).then(function (data) {
// do something with both sets of data here
console.log(data);
}).catch(function (error) {
// if there's an error, log it
});
I seem to be having an issue with getting the expected response from a fetch call within a firebase cloud function. I'm sure it's due to my lack of knowledge on how the responses, promises, etc. work.
I'm trying to use atlassian crowd's rest api for SSO. If I use postman, I can get the desired results from the request. So I know that part of it is working.
What led me to using a cloud function is that making the same request using fetch was resulting in CORS issues from localhost. I figured if I can take the browser out of the equation, then the CORS issues would disappear. Which they have, but I'm not getting the desired response.
My cloud function looks like this:
const functions = require('firebase-functions');
const fetch = require('node-fetch');
const btoa = require('btoa');
const cors = require('cors')({origin:true});
const app_name = "app_name";
const app_pass = "app_password";
exports.crowdAuthentication = functions.https.onRequest((request, response)=>
{
cors(request, response, () =>{
let _uri = "https://my.server.uri/crowd/rest/usermanagement/1/session";
let _headers = {
'Content-Type':'application/json',
'Authorization':`Basic ${btoa(`${app_name}:${app_pass}`)}`
}
let _body = {
username: request.body.username,
password: request.body.password
}
const result = fetch(_uri, {
method: 'POST',
headers: _headers,
body: JSON.stringify(_body),
credentials: 'include'
})
response.send(result);
})
})
I'm then making the call in my application using fetch to the firebase endpoint and passing the username/password:
fetch('https://my.firebase.endpoint/functionName',{
method: 'POST',
body: JSON.stringify({username:"myusername",password:"mypassword"}),
headers: {
'Content-Type':'application/json'
}
})
// get the json from the readable stream
.then((res)=>{return res.json();})
// log the response - {size:0, timeout:0}
.then((res)=>
{
console.log('response: ',res)
})
.catch(err=>
{
console.log('error: ',err)
})
Thanks for looking.
Edit of May 2020
Note that request-promise is deprecated and I recommend to use axios.
Update following our discussion in the comments below
It appears that it doesn't work with the node-fetch library and that you should use another library like request-promise.
Therefore you should adapt your code as follows:
//......
var rp = require('request-promise');
exports.crowdAuthentication = functions.https.onRequest((request, response) => {
cors(request, response, () => {
let _uri = "https://my.server.uri/crowd/rest/usermanagement/1/session";
let _headers = {
'Content-Type': 'application/json',
'Authorization': `Basic ${btoa(`${app_name}:${app_pass}`)}`
}
let _body = {
username: request.body.username,
password: request.body.password
}
var options = {
method: 'POST',
uri: _uri,
body: _body,
headers: _headers,
json: true
};
rp(options)
.then(parsedBody => {
response.send(parsedBody);
})
.catch(err => {
response.status(500).send(err)
//.... Please refer to the following official video: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
});
});
});
Initial answer with node-fetch
The fetch() method is asynchronous and returns a Promise. You therefore need to wait this Promise resolves before sending back the response, as follows:
exports.crowdAuthentication = functions.https.onRequest((request, response)=>
{
cors(request, response, () =>{
let _uri = "https://my.server.uri/crowd/rest/usermanagement/1/session";
let _headers = {
'Content-Type':'application/json',
'Authorization':`Basic ${btoa(`${app_name}:${app_pass}`)}`
}
let _body = {
username: request.body.username,
password: request.body.password
}
fetch(_uri, {
method: 'POST',
headers: _headers,
body: JSON.stringify(_body),
credentials: 'include'
})
.then(res => {
res.json()
})
.then(json => {
response.send(json);
}
.catch(error => {
//.... Please refer to the following official video: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
});
})
})
In addition, note that you need to be on the "Flame" or "Blaze" pricing plan.
As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google.com/pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)