I am trying to write a simple GET request that returns the JSON data at https://hacker-news.firebaseio.com/v0/item/160705.json
I have tried a number of things and nothing seems to work. (I'm on the paid Firebase plan that allows requests to external API's). I write the function, then run firebase deploy and execute the function but it either times out or throws another error.
As a test, this simple HTTP call works fine:
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send('test');
})
But when I try to run the following, hitting the HN API, it times out:
exports.helloWorld = functions.https.onRequest((request, response) => {
request.get('https://hacker-news.firebaseio.com/v0/item/160705.json', function (error, res, body) {
if (!error && res.statusCode == 200) {
console.log(body) // Print the google web page.
}
return response.send("") // this terminates the function
})
})
EDIT
The firebase log for the above function says:
Function execution started
Function execution took 60002 ms, finished with status: 'timeout'
I've also tried a couple other things, for example:
const options = {
host: 'hacker-news.firebaseio.com',
path: '/v0/item/160705.json'
};
// make the request
exports.hackerNews = functions.https.onRequest(options, (resp) => {
console.log(resp)
});
but that fails with a 500 Error: could not handle the request and Referrer Policy: no-referrer-when-downgrade
It shouldn't be this difficult to write a simple GET request in firebase functions, so I must be doing something dumb. Thanks.
I figured it out:
exports.helloWorld = functions.https.onRequest((req, res) => {
request.get('https://hacker-news.firebaseio.com/v0/item/160705', (error, response, body) => {
if (!error && response.statusCode === 200) {
return res.send(body);
}
return res.send('ERROR: ' + error.message);
})
});
Apparently you have to return something on success or error, you just can't execute another function like console.log().
Related
This question already has answers here:
nodejs - How to promisify http.request? reject got called two times
(6 answers)
Closed 1 year ago.
const http = require("http");
async function sendRequest(url) {
url = new URL(url);
const requestDetails = {
'hostname': url.hostname,
'port': url.port || 80,
'path': url.pathname,
'method': 'GET'
};
const req = await new Promise((resolve, reject) => {
const request = http.request(requestDetails, response => {
const status = response.statusCode;
if (status === 200 || status === 201) {
console.log("SUCCESS");
resolve(request);
} else {
console.log("ERROR");
reject(`Status code returned was ${status}`);
}
});
});
req.end();
}
sendRequest('http://httpbin.org/get');
It works when req.end() is inside the promise, but after passing the request out then execute req.end(), the console is just holding without any response. I tried to compare "req === request" by a middle variable, it returned true. Why doesn't moving end() out work? Shouldn't these two object the same?
The purpose of the req.end() is to finish the request. We might be cautious that if any body part is unsent or might in progress, it will flush them in the stream, or if any request is chunked, this will send to terminating.
I have implemented your same code in a bit different and cleaner way. Below way might help to reuse the same code for multiple apis.
const http = require("http");
/**
* #description call the http request
*/
async function doHttp(requestDetails){
return new Promise((resolve, reject)=>{
http.request(requestDetails, response => {
const status = response.statusCode;
response.setEncoding("utf-8");
if (status === 200 || status === 201) {
console.log("SUCCESS");
response.on('data', data => {
return resolve(data);
});
} else {
console.error("ERROR");
return reject(new Error("emptyData"));
}
}).on('error', (err) => {
// Catch the error if occured in request
console.error(`problem with request: ${e.message}`);
return reject(err);
}).end();
});
}
/**
* #description sending the request
*/
async function doSend(url) {
url = new URL(url);
const requestDetails = {
'hostname': url.hostname,
'port': url.port || 80,
'path': url.pathname,
'method': 'GET'
};
const data = await doHttp(requestDetails)
console.log(data);
}
doSend('http://httpbin.org/get');
At last, we could say req.end() is required to finish up any request. It completely depends on us, how we can implement a method.
An alternate solution might be this native https module is such as Axios, superagent, got, node-fetch. They provide a wrapper over the native nodejs code which might help us to control to handle an error and response.
You should move the request.end call inside the promise otherwise it just newer gets called because you will be waiting for a promise that is newer resolved because the request is not send.
Also you should reject the promise in case request object emits error event.
I have written this function which allows users to add images to the server. However, my alerts don't seem to be working. I've tried to console.log the alert messages instead of having them as alerts but that doesn't seem to work either. Any tips would be great.
client.js
async function picsub(event){
event.preventDefault();
var image=document.getElementById('imageFile');
const formData = new FormData();;
formData.append('image',image.files[0]);
let i={
method:'POST',
body:formData,
}
fetch('http://127.0.0.1:8090/pics', i).then((response) => {
return response.text();
}).then((response) => {
if (response === 'success') {
alert('pic added');
} else {
alert('An error has occurred');
}
}).catch((e) => {
alert('An error has occurred');
form.reset();
}
const form = document.getElementById("form");
form.addEventListener("submit", picsub);
server.js
const app = express();
const port = 8090;
let pictures=[];
app.post('/pics', (req, res) => {
const pic = req.body;
console.log(pic);
pictures.push(pic);
res.status(201);
});
In your express, you only setup the status, but you didn't send any message from server. So, your then or catch in fetch will be executed.
then will be executed when you receive the response from server.
catch will be executed when you have failed connection between client and server.
In your case, you didn't send any response from server, fetch function will wait for response until timeout. But, you didn't setup any timeout. That means there are no response from server or any failed connection. So, your then or catch in fetch will not be executed. Unless, you shut down the server. But, browser will help you setup the default timeout, such as Chrome. It use 300 seconds as timeout.
So, you need to send message with res.end("success") like below.
app.get('/pics', function (req, res) {
const pic = req.body;
console.log(pic);
pictures.push(pic);
res.status(201).end("success");
})
And in your client.js, you should complete the bracket in the fetch function.
fetch('http://127.0.0.1:8090/pics', i).then((response) => {
return response.text();
}).then((response) => {
if (response === 'success') {
alert('pic added');
} else {
alert('An error has occurred');
}
}).catch((e) => {
alert('An error has occurred');
}) // ===> here
Try not using async here. Also, try console.log() some things within the function, especially near the alert(). This way, you know it even gets to the alert()
I have this code in my NodeJS. If I put the return before the request.post call I get the response but inside the call back I get {data: null}.
exports.addItem = functions.https.onCall((req, context)=>{
request.post(
{url: submitUrl,
form: req},
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
return {result: "OK"};
}
else{
return {result: "Not OK"}
}
}
);
});
if it helps, I run this code inside firebase and I am calling it with firebase functions in my JS
return will cause the function to end, but won't send anything to the user when it's an asynchronous callback like that. There is no calling function that you control for the value to be returned to.
I'm not sure what you're trying to achieve, but using a value with return in this context will result in the value being lost.
If you need to improve your understanding of the asynchronous callback idiom in Node.js, maybe check out https://blog.risingstack.com/node-hero-async-programming-in-node-js/.
You should use async/await to fix this issue
exports.addItem = functions.https.onCall(async (req, res)=>{
await request.post(
{url: submitUrl,
form: req},
function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
return {result: "OK"};
}
else{
return {result: "Not OK"}
}
}
);
});
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I am writing a class in JavaScript that sends HTTP requests for a specific URL. I'm trying to test that class with Mocha but for some reason, the method fetchUrl() returns undefined. I can't seem to figure out why. I literally started writing in JavaScript a day ago, therefore I am still trying to learn and adjust to it.
fetchUrl () {
var request = require('request')
var res
request(this.url, function (error, response, body) {
console.log('error:', error) // Print the error if one occurred
if (response.statusCode !== 200) {
console.log('received status code other than 200 OK')
this.error = true
}
res = response
console.log('statusCode:', response && response.statusCode) // Print the response status code if a response was received
// console.log('body:', body) // Print the HTML for the requested url.
this.html = body
})
return res
}
describe('Test Http request to google.com', function () {
it('should return 200', function (done) {
assert.equal(httpCon.fetchUrl().statusCode, 200)
done()
})
})
You should use Nock libray to mocking HTTP request.
const axios = require('axios');
module.exports = {
getUser(username) {
return axios
.get(`https://api.github.com/users/${username}`)
.then(res => res.data)
.catch(error => console.log(error));
}
};
And here test case:
describe('Get User tests', () => {
beforeEach(() => {
nock('https://api.github.com')
.get('/users/octocat')
.reply(200, response);
});
});
For more details, you can look at this: mocking-http also look into this answer of SO. source
I think you should only return res inside the callback, otherwise it will return undefined since the program keeps running...
https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
You may want to take a look on callback funcionality
I need to call an API continuously from server side itself, so that it is called 24/7 every second. How can I achieve this?
I tried it as shown below in server.js, but getting 'TypeError: request.put is not a function'.
app.get('/',function(request,response){
setInterval(function(){
request.put('http://localhost:4242/changepaidstatus', function(error,response){
if (error){
console.log(error);
}
else{
console.log(response);
}
});
}, 1000);
});
setInterval() will let you repeat some function every second.
setInterval(() => {
// will execute every second
}, 1000);
As for calling the API, you can use the request() module to make any http request you would like. Here's an example from their doc:
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
}
})