How to get JSON data from Firebase Cloud Function - javascript

Is there something wrong in my code? I always get the error Error: could not handle the request. Here's the code of my Cloud Function:
exports.helloWorld = functions.https.onRequest((request, response) => {
cors(request, response, () => {
response.set("Access-Control-Allow-Origin", "*");
response.set("Access-Control-Allow-Methods", "GET");
response.set("Access-Control-Allow-Headers", "Content-Type");
response.set("Access-Control-Max-Age", "3600");
let dock = [];
const snapshot = db.collection("users").get()
snapshot.docs.forEach((val) => {
dock.push(val.id);
});
});
});

I don't know if you are trying to return dock as response, but if you do, try this out:
exports.helloWorld = functions.https.onRequest((request, response) => {
cors(request, response, () => {
response.set("Access-Control-Allow-Origin", "*");
response.set("Access-Control-Allow-Methods", "GET");
response.set("Access-Control-Allow-Headers", "Content-Type");
response.set("Access-Control-Max-Age", "3600");
let dock = [];
db.collection("users").get().then(snapshot => {
snapshot.docs.forEach((val) => {
dock.push(val.id);
});
return dock;
})
.catch(errors => {
console.log(errors);
return false;
})
});
});
The get() returns a promise that you have to solve.
Also it is a good idea to put a catch() at the end to see errors appear, when executing your code.

A HTTPS triggered Cloud Functions (like the one in your question) runs until it sends a response to the client, or until it times out. Since your code never sends a response to the client, it times out. And that's why the client gives you and error.
I assume you want to send the document IDs back to the client, so that'd be something like:
exports.helloWorld = functions.https.onRequest((request, response) => {
cors(request, response, () => {
response.set("Access-Control-Allow-Origin", "*");
response.set("Access-Control-Allow-Methods", "GET");
response.set("Access-Control-Allow-Headers", "Content-Type");
response.set("Access-Control-Max-Age", "3600");
let documentIds = [];
db.collection("users").get().then((snapshot) => {
snapshot.docs.forEach((document) => {
documentIds.push(document.id);
});
res.send(JSON.stringify(documentIDs));
});
});
});

Related

Proxy error 504 after reading the request body

I was trying to read http request body, but I got 504 errors, it reminded me that proxy didn't config correctly. I think the issue is because of I read and access the request with process request.read().toString() How do I reset http request after done the request.body.toString()?
const proxyWithPromise = (
req: Request,
res: Response,
next: NextFunction
): Promise<void> => {
const events = req.body.toString();
console.log('events', events);
const goodData = filterJob(events);
if (goodData) {
return new Promise<void>(
(resolve) => {
res.on('close', resolve);
proxy(req, res, (err) => {
if (next != null) {
console.error(err);
next(err);
}
});
}
);
}
return new Promise<void>(() => {});
My goal is if its goodData after the filter work, I process the Promise; otherwise I would do nothing but complete a Promise.
Thanks a lot!!!
one approach I think is to do a deep copy with request, but its very expensive.

Passing res wrapped in object into callback fails

I have a temporary server that looks like this:
import http from 'http'
export default function tempServer(
port: number
): Promise<{ req: http.IncomingMessage; res: http.ServerResponse }> {
return new Promise((resolve) => {
const server = http
.createServer(function (req, res) {
resolve({ req, res })
// This works
// res.write('Hey!')
// res.end()
server.close()
})
.listen(port)
})
}
And I am trying to manipulate res by calling res.write in another function:
function () {
const server = tempServer(parseInt(process.env.AUTH_PORT) || 5671)
return server.then(({req, res}) => {
const data = url.parse(req.url, true).query
...
res.write('Error: ' + data.error)
res.end()
...
})
}
The result is that res.write and res.write have no effect. I am sure the issue has something to do with contexts and bindings but I am having trouble going about it. Anyone willing to indulge me?

Do we need to wrap app.get in async function

I've come across this code
(async () => {
app.get('/health', (req: Request, res: Response) => {
res.send();
});
more endpoints here
....
})();
I don't get why we need to wrap app.get in async here, is that necessary?
Probably not, but we'd need more context to be sure. As shown, no, there's no point.
It may be that they were relying on information they only got asynchronously for setting up the routes and wanted to use await rather than .then/.catch when using that information, e.g.:
(async () => {
try {
app.get('/health', (req: Request, res: Response) => {
res.send();
});
const moreRoutes = await getMoreRoutes();
// −−−−−−−−−−−−−−−−^^^^^
for (const route of moreRoutes) {
// ...set up more routes
}
} catch (e) {
// Handle/report error
}
})();
If so, hopefully they have a try/catch around the entire body (as shown above) or a .catch at the end like this:
(async () => {
app.get('/health', (req: Request, res: Response) => {
res.send();
});
const moreRoutes = await getMoreRoutes();
// −−−−−−−−−−−−−−−−^^^^^
for (const route of moreRoutes) {
// ...set up more routes
}
})().catch(e => {
// Handle/report error
});
async/await can make it much easier (IMHO) to read code. But the above can be done with .then/.catch as well:
app.get('/health', (req: Request, res: Response) => {
res.send();
});
getMoreRoutes()
.then(moreRoutes => {
// ...set up more routes
})
.catch(e => {
// Handle/report error
});

Google Cloud Function async with multiple fetch requests

I'm new to both GCF and Javascript async and have been struggling with this. I perform a fetch call initially and then pass that response as a parameter to a second function which then also performs a separate fetch call.
During the second function, my empty initialized json gets properties added to it, and when that function completes, I want to notify the exports.helloHttp to then do res.end and terminate.
I've tried chaining an additional empty then() but it doesn't seem to be working.
My code:
var json = {}; // <- gets properties added to it during secondFunction()
exports.helloHttp = (req, res) => {
fetch("firstfetchurl.com",requestOptions)
.then(result => result.json())
.then(response => {
// next take the result and create a new product
return secondFunction(response);
})
.catch(error => console.log('error', error));
// res.end(JSON.stringify(json)); <- this is what I want my cloud function to output, but only after secondFunction completes
};
Here is the code that would do what you want (replace the fetch URLs and set the appropriate options)
const fetch = require('node-fetch');
exports.helloHttp = async (req, res) => {
return fetch("https://jsonplaceholder.typicode.com/users/1/albums") // First fetch
.then(firstFetchResponse => firstFetchResponse.json())
.then(firstFetchResponse => secondFunction(firstFetchResponse)) // Second fetch
.then(secondFunctionResponse => secondFunctionResponse.json())
.then(finalResponse => res.json(finalResponse)) // This line sends your response to the client
.catch(error => { console.error('Error', error); res.status(500).send('Server Error') }); // In case an error, log and send an error response
};
async function secondFunction(data) {
// Logic of your second function. Here just does another fetch using the data from the first request
let firstAlbumId = data[0].id
return fetch(`https://jsonplaceholder.typicode.com/albums/${firstAlbumId}/photos`);
}
The same function can use an await like this
exports.helloHttp = async (req, res) => {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/users/1/albums") // Note the await on this line
.then(result => result.json())
.then(firstFetchResponse => secondFunction(firstFetchResponse))
.then(secondFetchResponse => secondFetchResponse.json());
res.json(response); // Finally you are sending the response here.
} catch (error) {
console.error(error);
res.status(500).send('Server Error');
}
};
Finally you would also need to make sure that the package.json has the dependency for node-fetch
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"node-fetch": "^2.6.0" // This line must be there
}
}
For sending the JSON response, it uses this method.
result.json() is not an asynchronous operation, therefore you don't need to use a then() block. The following should do the trick;
exports.helloHttp = (req, res) => {
fetch("firstfetchurl.com",requestOptions)
.then(result => {
return secondFunction(result.json());
})
.catch(error => console.log('error', error));
//...
Note that, depending on the exact goal of you helloHttp function, you may need to return the entire promises chain, as follows:
exports.helloHttp = (req, res) => {
return fetch("firstfetchurl.com",requestOptions) // Note the return
.then(result => {
return secondFunction(result.json());
})
.catch(error => console.log('error', error));
//...

Axios code not running after `.then`

I am new to react/nodejs/express/javascript and have encountered the following problem:
I want to get a number, then post that number + 1, then I want to create a new js object using that number(newFreshId) and I want to add it to add that event to my schedulerData. When I try running the code, I can get and post to /api/num, but I everything after the .then(function(response) { doesnt appear to run.
I wanted to do this sequentially, so I used .then after every task so that I would not have encountered a problem.
I also tried to remove all the .thens in favor of a while loop that waits for the value to change. This also did not work.
CODE:
CLIENT:
this.newEvent = (schedulerData, slotId, slotName, start, end, type, item) => {
let newFreshId = 0;
let newEvent = {}
axios.get("/api/num").then(function(response) {
newFreshId = response.data[0] + 1;
// console.log(newFreshId);
}).then(function() {
axios.post("/api/num", {
id: newFreshId
}).then(function(response) {
console.log(response)
// handle success
newEvent = {
id: newFreshId,
title: this.state.title,
start: start,
end: end,
resourceId: slotId
};
schedulerData.addEvent(newEvent);
this.setState({
viewModel: schedulerData
});
// while(JSON.stringify(newEvent) === '{}'){
// console.log('waiting')
// }
console.log(newEvent)
schedulerData.addEvent(newEvent);
console.log(newEvent)
this.setState({
viewModel: schedulerData
});
})
})
};
SERVER:
app.get('/api/num', function(req, res) {
//console.log(require('./number.json'))
var fs = require('fs')
fs.readFile('./number.json', {encoding: 'utf-8'}, function(err,data){
if (!err) {
//console.log('received data: ' + JSON.parse(data));
res.json(JSON.parse(data))
} else {
console.log(err);
}})
})
app.post('/api/num', function(req, res) {
var id = req.body.id
var fs = require('fs');
fs.writeFileSync("./number.json", "[ "+id+" ]", function(err) {
if(err) {
return console.log(err);
}
res.status(200)
})
})
Thanks for all the help :)
fs.writeFileSync doesn't have a callback, so the function you're adding never gets executed: https://nodejs.org/api/fs.html#fs_fs_writefilesync_file_data_options
This means the response is never sent back to the client, and the axios promise is never resolved.
Try using fs.writeFile with a callback: https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback
It's also a good idea to send a response in the case of an error, too.
app.post('/api/num', function(req, res) {
var id = req.body.id
var fs = require('fs');
fs.writeFile("./number.json", "[ "+id+" ]", function(err) {
if(err) {
console.log(err);
return res.status(200)
}
res.status(200)
})
})
Finally, though it wouldn't help you in this situation, you should add a .catch to the very tail end of your axios chain. Any errors that take place in the promise chain will wind up there.
An example:
axios.get(specs).then(someFunction).catch(e => console.error(e));
for me I was facing the same issue and I figured it out that because I'm using some redirection after making a post request
window.location.href = "/{redirect to some rout}"
this makes the console change immediately so I couldn't see the then response unless I removed the redirection.
I too faced an issue where I wasn't able to run the .then() or .catch() code
const fetch = async() => {
axios.get("https://jsonplaceholder.typicode.com/todos/1")
.then((res) => {
console.log(res)
})
.catch(err => {
console.log(err)
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.24.0/axios.min.js"></script>
All I did was add async to the function and it started working
const axios = require('axios').default;
use it like this

Categories

Resources