Firebase Firestore return QuerySnapshot - javascript

I just started to play with the firebase cloud function and firestore but when I'm using firestore inside the firebase cloud function (as below code) it's return and QuerySnapshot instead of return data. If anyone has got this issue before and solved it already then tell me. It would help me to resolve this issue too.
Thanks.
export async function allRestaurants(req: Request, res: Response) {
try {
// const { id } = req.params
const restaurantsRef = admin.firestore().collection('restaurants');
const snapshot = await restaurantsRef.get();
console.log(">>>>>>>>>", snapshot);
return res.status(200).send({ data: { restaurants: snapshot } })
} catch (err) {
return handleError(res, err)
}
}

It is normal that you get a QuerySnapshot, since the get() method returns a Promise that resolves with a QuerySnapshot.
It's up to you to generate the content you want to send back to the Cloud Function consumer.
For example, you can use the forEach() method to loop over the QuerySnapshot, or, as shown below, use the docs array.
export async function allRestaurants(req: Request, res: Response) {
try {
// const { id } = req.params
const restaurantsRef = admin.firestore().collection('restaurants');
const snapshot = await restaurantsRef.get();
const responseContent = snapshot.docs.map(doc => doc.data());
return res.status(200).send({ data: { restaurants: responseContent } })
} catch (err) {
return handleError(res, err)
}
}

Related

Is it possible to call to APIs inside a react-router loader function

I'd like to know if it's possible to make 2 API calls inside a loader function if I am using react-router 6. My ideas was to create an object based on these 2 calls and destruct the object in the rendering component like this:
function MainComponent (){
const {data , reservation} = useRouteLoaderData('room-details');
..
..
}
export default MainComponent;
export async function loader({request, params}) {
const id = params.roomId;
const response = await fetch ('http://localhost:8080/rooms/' + id);
const response2 = await fetch('http://localhost:8080/rooms/reservation/' + id)
const megaResponse = {
data: response, //i tried data:{respose} It ain't work
reservation: response2,
};
if (!response.ok) {
throw json({message: 'Something Wrong'}, {status: 500});
}
else {
return megaResponse;
}
}
But i have no success output.
I'd really want to make these 2 call in one place, otherwise I will have to use useEffect in a child component. Not a good Idea I think.
Thanks
I suspect you are not returning the unpacked response, i.e. JSON. I suggest surrounding the asynchronous code in a try/catch and simply try to process the requests/responses. Unpack the JSON value from the response objects. Since it doesn't appear the requests are dependent on one another I recommend loading them into an array of Promises that can be run concurrently and awaited as a whole. If during any part of the processing a Promise is rejected or an exception thrown, the catch block will return the JSON error response to the UI, otherwise, the { data, reservation } object is returned.
const loader = async ({ request, params }) => {
const { roomId } = params;
try {
const [data, reservation] = await Promise.all([
fetch("http://localhost:8080/rooms/" + roomId),
fetch("http://localhost:8080/rooms/reservaton/" + roomId)
]).then((responses) => responses.map((response) => response.json()));
return { data, reservation };
} catch {
throw json({ message: "Something Wrong" }, { status: 500 });
}
};
I found the solution, I tried it and it worked. It is as follow:
function MainComponent (){
const [data , reservation] = useRouteLoaderData('room-details');
..
..
}
export default MainComponent;
export async function loader({request, params}) {
const id = params.roomId;
return Promise.all([
fetch ('http://localhost:8080/rooms/' + id),
fetch('http://localhost:8080/rooms/reservation/' + id)
])
.then(
([data, reservation]) =>
Promise.all([data.json(), reservation.json()]),
error => {throw json({message: 'Something Wrong'}, {status: 500});}
)
.then(([data, reservation]) => {
return [data, reservation];
});
}
Thanks

Why can't I return data after await a promise object in async function

I'm a newbie studying coding.
I tried to mock mysql database query, so I followed below code(https://techhelpnotes.com/node-js-mock-database-jest-using-ts-jest-utils/)
//database.js
const getDbConnection = () => {
const pool = mysql.createPool(DB_CONFIG);
return {
query : function (sql) {
return util.promisify(pool.query).call(pool, sql);
};
//controller.js
try {
let result = await db.query(sql);
res.status(200).json(result);
} catch (error) {
console.log(DB error, error);
}
It worked for me, but I thought I could return the query with await by using it in databse.js like the one below
query : async function(sql) {
try {
return await util.promisify(pool.query).call(pool, sql);
} catch (error) {
console.log(DB error, error);
}
}
so I thought I can use query function without error handling
let result = await db.query(sql);
But it doesn't work. What difference is there between the two codes that makes the above code work and the below now??
Many thanks!

Promises not getting resolved

I am running this asynchronous function in my React app -
const getMetaData = async (hashes: any) => {
console.log({ hashes });
try {
const data = hashes.map(async (hash: any) => {
const url = `http://localhost:3003/user/pinata/getmetadata/${hash}`;
const metadata = await axios.get(url);
return metadata.data.response;
});
console.log("data1", data);
const metadata = await Promise.all(data);
console.log('data2', metadata);
} catch (error) {
console.log('getMetaData Error', error);
}
};
console.log("data1", data) gives me -
data1 (12) [Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise]
The problem here is after I do a await Promise.all(data) I don't get data2 anywhere in the console. Maybe because the Promises are not even getting resolved?
Any idea what might be wrong?
Thanks in advance.
It seems that your code works fine when using SWAPI API so it can be that the API you use does not deliver data appropriately. I run the below code to test. Here's a link to codebox to play around with it if you want.
import axios from "axios";
const data = ["people", "planets", "starships"];
const getMetaData = async (hashes) => {
console.log({ hashes });
try {
const data = hashes.map(async (hash) => {
const url = `https://swapi.dev/api/${hash}`;
const metadata = await axios.get(url);
return metadata.data.results;
});
console.log("data1", data);
const metadata = await Promise.all(data);
console.log("data2", metadata);
} catch (error) {
console.log("getMetaData Error", error);
}
};
getMetaData(data);
With this code, it appears the most likely situation is that one of the promises in the loop is not resolving or rejecting. To confirm that, you can log every possible path with more local error handling so you can see exactly what happens to each request. I also added a timeout to the request so you can definitely find out if it's just not giving a response, but you can also see that by just looking at the logging for begin and end of each request in the loop:
function delay(msg, t) {
return new Promise((resolve, reject)) => {
setTimeout(() => {
reject(new Error(msg));
}), t);
});
}
const getMetaData = async (hashes: any) => {
console.log({ hashes });
try {
const data = hashes.map(async (hash: any, index: number) => {
try {
console.log(`Request: ${index}, hash: ${hash}`);
const url = `http://localhost:3003/user/pinata/getmetadata/${hash}`;
const metadata = await axios.get(url);
console.log(`Request: ${index}, result: ${metadata.data.response}`);
return metadata.data.response;
} catch (e) {
console.log(`Request: ${index} error: `, e);
throw e;
}
});
console.log("data1", data);
const metadata = await Promise.all(data.map((p: any, index: number) => {
return Promise.race(p, delay(`Timeout on request #${index}`, 5000));
});
console.log('data2', metadata);
} catch (error) {
console.log('getMetaData Error', error);
}
};
FYI, I don't really know Typescript syntax so if I've made any Typescript mistakes here, you can hopefully see the general idea and fix the syntax.

How can I push data to an array inside an async request to the database?

I am trying to make a request to the database (mongoDB) and save its return in a list of objects but the list is not getting filled. Here is the code
router.get('/all/:studentEmail', auth, async (req, res) => {
try {
const student = await Student.findOne({ email: req.params.studentEmail });
if (!student) {
return res.status(404).json({ msg: 'Student not found' });
}
var list = [];
student.exercises.map(async (exercise) => {
list.unshift(await Exercise.findById(exercise));
});
res.json(list);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
The database query await Exercise.findById(exercise) returns correctly the object, but res.json(list); returns empty. Do anyone know how to solve it?
The base issue is that res.json() executes way before student.exercises.map(async (exercise) => { completes. Putting await into map doesn't wait for each and every item in the async loop to process. Either use something like Promise.all() or use a for loop (other strategies can be used also). Decide which to use based on whether you can process in parallel or need to process in series. Try the following using Promise.all to execute async requests parallel using then on each Promise to execute an operation against list:
router.get("/all/:studentEmail", auth, async (req, res) => {
try {
const student = await Student.findOne({ email: req.params.studentEmail });
if (!student) {
return res.status(404).json({ msg: "Student not found" });
}
var list = [];
await Promise.all(
student.exercises.map((exercise) =>
Exercise.findById(exercise).then((result) => list.unshift(result))
)
);
res.json(list);
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
});
Also, an alternative to unshift and just return the results if they are not nested, if they are nested you can consider flat():
const list = await Promise.all(
student.exercises.map((exercise) => Exercise.findById(exercise))
);
return res.json(list);
Hopefully that helps!

Async function returning Promise even after calling Await

I've been trying to create a helper function to return a document's data from the Firebase database using Nodejs:
module.exports = async (collectionName, documentId, res) => {
const collection = db.doc(`/${collectionName}/${documentId}`);
try {
const targetedDocument = await collection.get();
if (!targetedDocument.exists) {
return res.status(404).json({
error: true,
message: `the document ${documentId} is not exists.`,
});
}
return targetedDocument.data();
} catch (error) {
return res.status(500).json({ error: true, message: error });
}
};
But when I tried to use it, it always returns back a promise:
const documentFinder = require('./helper-function-path');
router.post('post',(req,res)=>{
const requiredDocument = documentFinder("post", "Izkn12IMnayzokLqe",res);
console.log(requiredDocument); //returned a promise rather than an object document
})
What am I doing wrong here? Some pointer would be very much appreciated. Thank you.
async functions, by definition, always return a promise. You can't make an asynchronous function to be synchronous simply by wrapping it. The caller will always still have the promise to deal with. You can deal with the returned promise in your express route by making its callback also async, and awaiting the result of the function call:
router.post('post', async (req,res)=>{
const requiredDocument = await documentFinder("post", "Izkn12IMnayzokLqe",res);
console.log(requiredDocument);
})
Please try out:
module.exports = async (collectionName, documentId, res) => {
const collection = db.doc(`/${collectionName}/${documentId}`);
try {
const targetedDocument = await collection.get();
if (!targetedDocument.exists) {
return res.status(404).json({
error: true,
message: `the document ${documentId} is not exists.`,
});
} else {
return targetedDocument.data();
}
} catch (error) {
return res.status(500).json({ error: true, message: error });
}
};

Categories

Resources