So i'm currently trying to pull data inside Nuxt from Cloud Firestore via:
async asyncData(){
return firebase.firestore().collection('test').doc().get()
.then((result) => {
result.forEach(doc => {
console.log(doc.data())
})
})
}
which results in the following error:
async asyncData() {
return _plugins_firebase_config__WEBPACK_IMPORTED_MODULE_0__["default"].firestore().collection('test').doc().get().then(result => {
result.forEach(doc => {
console.log(doc.data());
});
});
}
with the error message:
TypeError
result.forEach is not a function
Pulling data without forEach works fine. So if i specify the doc by the name and get data only for one document, everything works as expected.
However, since i need the whole collection of the data, i have to split up the data via forEach. Any ideas what could cause that?
Same example works in VueJS itself just fine.
I've used nuxt cli to generate a new project, so the project setup should be fine.
First get and modify Your data, then return it.
Modify Your code so it looks like this:
async asyncData () {
let data = []
await firebase.firestore()
.collection('test')
.doc()
.get()
.then((result) => {
result.forEach(doc => {
data.push(doc.data())
})
})
return data
}
Related
I am using CK-editor 5 and trying to add "upload image" function. I created an adapter with the following example provided by Piyush (Source: https://noteyard.piyushdev.xyz/blogpost/62596290751435313d7f85b5). However, since I use firestore as backend storage and write all async functions with async/await. Is there any way I can re-write all these codes with async/await and store it in a firestore collection called "Images"? Document name can be random ID, and data key value can be "image". I tried to write by myself but failed. Besides, I only get an empty HTML page saying "Cannot GET /api/blogs/uploadImg" while checking out the API URL example provided, and I can't check how the API structure looks like".
full code:
function uploadAdapter(loader) {
return {
upload: () => {
return new Promise((resolve, reject) => {
const body = new FormData();
loader.file.then((file) => {
body.append("uploadImg", file);
fetch(`${API_URl}/${UPLOAD_ENDPOINT}`, {
method: "post",
body: body
})
.then((res => res.json()))
.then((res) => {
resolve({ default: `${API_URl}/${res.url}` })
})
.catch((err) => {
reject(err);
})
})
})
}
}
}
function uploadPlugin(editor) {
editor.plugins.get("FileRepository").createUploadAdapter = (loader) => {
return uploadAdapter(loader);
}
}
API URL with "Cannot GET /api/blogs/uploadImg":
const API_URl = "https://noteyard-backend.herokuapp.com"
const UPLOAD_ENDPOINT = "api/blogs/uploadImg";
I am trying to update some values that I will enter from my Flutter app to FireStore using Cloud Functions. Here is my code so far:
This is my Cloud function (in JavaScript, index.js) to update a document in FireStore:
exports.update = functions.https.onRequest((req, res) => {
const getNewPercentage = req.body;
const getDocument = admin.firestore().collection('waterpercentage').doc('percentage');
getDocument.get().then((doc) => {
if(doc.exists) {
getDocument.update({'percentage': getNewPercentage}).catch(err => {
console.log("Error: ", err);
res.send("500");
})
}
}).catch(err => {
console.log("Error: ", err)
res.send("500");
});
res.send(200);
})
In Flutter, here's what I tried:
Future<void> updateWaterPercentage() async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('update');
//final results = await callable.call(<dynamic, int>{'percentage' : percentage.round()});
log("Calling percentage here: ");
log(percentage.round().toString());
dynamic resp = await callable.call(<double, dynamic> {
percentage : percentage.round(),
});
When I call updateWaterPercentage() from a Button in Flutter, the data doesn't get updated in FireStore. I also tried using:
CloudFunctions.instance.call(
functionName: "update",
parameters: {
"percentage": percentage.round(),
}
);
However, even though I imported 'package:cloud_functions/cloud_functions.dart'; on top, Flutter doesn't recognize CloudFunctions. How can I get the code to call update that takes in a parameter to correctly update a value in Firestore?
You are mixing Callable Cloud Functions and HTTPS Cloud Functions.
Your Cloud Function code corresponds to an HTTP one (functions.https.onRequest((req, res) => {(...)}) but the code in your app declares and calls a Callable one (HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('update');).
In addition, in your HTTPS Cloud Function code, you send back a response before the asynchronous operation is complete (see the last line res.send(200);).
So, to fix the problem:
You could call the HTTPS Cloud Function from your flutter app with the http package;
But the best would probably be to adapt your Cloud Function to be a Callable one in order to get the advantages of a Callable, including the use of the cloud_functions package which makes very easy to call the CF from your app. Something along the following lines.
exports.update = functions.https.onCall((data, context) => {
const getNewPercentage = data.percentage;
const documentRef = admin.firestore().collection('waterpercentage').doc('percentage');
return documentRef.get()
.then((doc) => {
if (doc.exists) {
return documentRef.update({ 'percentage': getNewPercentage });
} else {
throw new Error('Doc does not exist');
}
})
.then(() => {
return { result: "doc updated" };
})
.catch(err => {
console.log("Error: ", err)
// See the doc: https://firebase.google.com/docs/functions/callable#handle_errors
});
});
So I am working on a upload function for multiple images in an array. After a lot of struggling I have finally got my upload function to work and the images are showing up in the Firebase Database. However I have yet to find out a working way to make sure my upload function completes before continuing.
Below is the part were I am calling the upload function and try to store the response in uploadurl, the uploadurl variable is later used in the dispatch function to store the url with other data.
try {
uploadurl = await uploadImages()
address = await getAddress(selectedLocation)
console.log(uploadurl)
if (!uploadurl.lenght) {
Alert.alert('Upload error', 'Something went wrong uploading the photo, plase try again', [
{ text: 'Okay' }
]);
setIsLoading(true);
return;
}
dispatch(
So the image upload function is below. This works to the point that the images are uploaded, however the .then call to get the DownloadURL is not started correctly and the .then images also is not working.
uploadImages = () => {
const provider = firebase.database().ref(`providers/${uid}`);
let imagesArray = [];
try {
Promise.all(photos)
.then(photoarray => {
console.log('all responses are resolved succesfully')
for (let photo of photoarray) {
let file = photo.data;
const path = "Img_" + uuid.v4();
const ref = firebase
.storage()
.ref(`/${uid}/${path}`);
var metadata = {
contentType: 'image/jpeg',
};
ref.putString(file, 'base64', metadata).then(() => {
ref
.getDownloadURL()
.then(images => {
imagesArray.push({
uri: images
});
console.log("Out-imgArray", imagesArray);
})
})
};
return imagesArray
})
} catch (e) {
console.error(e);
}
};
So I want to return the imagesArray, AFTER, all the photos are uploaded. So the imagesArray is then set as uploadURL in the first function? After all images URL are set in imagesArray and passed to uploadURL, only then my dispatch function to upload the rest of the data should continue. How can I make sure this is happening as expected?
I have changed this so many times now because I keep getting send to different ways of doing this that I am completely at a loss how to continue now :(
Most of your uploadImages() code was correct, however in many places you didn't return the promise from each asynchronous action.
Quick sidestep: Handling many promises
When working with lots of asynchronous tasks based on an array, it is advised to map() the array to an array of Promises rather than use a for loop. This allows you to build an array of promises that can be fed to Promise.all() without the need to initialise and push to another array.
let arrayOfPromises = someArray.map((entry) => {
// do something with 'entry'
return somePromiseRelatedToEntry();
});
Promise.all(arrayOfPromises)
.then((resultsOfPromises) => {
console.log('All promises resolved successfully');
})
.catch((err) => {
// an error in one of the promises occurred
console.error(err);
})
The above snippet will fail if any of the contained promises fail. To silently ignore individual errors or defer them to handle later, you just add a catch() inside the mapped array step.
let arrayOfPromises = someArray.map((entry) => {
// do something with 'entry'
return somePromiseRelatedToEntry()
.catch(err => ({hasError: true, error: err})); // silently ignore errors for processing later
});
Updated uploadImages() code
Updating your code with these changes, gives the following result:
uploadImages = () => {
const provider = firebase.database().ref(`providers/${uid}`);
// CHANGED: removed 'let imagesArray = [];', no longer needed
return Promise.all(photos) // CHANGED: return the promise chain
.then(photoarray => {
console.log('all responses are resolved successfully');
// take each photo, upload it and then return it's download URL
return Promise.all(photoarray.map((photo) => { // CHANGED: used Promise.all(someArray.map(...)) idiom
let file = photo.data;
const path = "Img_" + uuid.v4();
const storageRef = firebase // CHANGED: renamed 'ref' to 'storageRef'
.storage()
.ref(`/${uid}/${path}`);
let metadata = {
contentType: 'image/jpeg',
};
// upload current photo and get it's download URL
return storageRef.putString(file, 'base64', metadata) // CHANGED: return the promise chain
.then(() => {
console.log(`${path} was uploaded successfully.`);
return storageRef.getDownloadURL() // CHANGED: return the promise chain
.then(fileUrl => ({uri: fileUrl}));
});
}));
})
.then((imagesArray) => { // These lines can
console.log("Out-imgArray: ", imagesArray) // safely be removed.
return imagesArray; // They are just
}) // for logging.
.catch((err) => {
console.error(err);
});
};
I have created a callable Cloud Function to read data from Firebase and send back the results to the client, however, only "null" is being returned to the client.
exports.user_get = functions.https.onCall((data, context) => {
if (context.auth && data) {
return admin.firestore().doc("users/" + context.auth.uid).get()
.then(function (doc) {
return { doc.data() };
})
.catch(function (error) {
console.log(error);
return error;
})
} return
});
I just reproduced your case connecting from a Cloud Function with a Firestore database and retriving data. As I can see you are trying to access the field in a wrong way when you are using "users/" + context.auth.uid, the method can't find the field so its returning a null value.
I just followed this Quickstart using a server client library documentation to populate a Firestore database and make a Get from it with node.js.
After that i followed this Deploying from GCP Console documentation in order to deploy a HTTP triggered Cloud Function with the following function
exports.helloWorld = (req, res) => {
firestore.collection('users').get()
.then((snapshot) => {
snapshot.forEach((doc) => {
console.log(doc.id, '=>', doc.data().born);
let ans = {
date : doc.data().born
};
res.status(200).send(ans);
});
})
And this is returning the desired field.
You can take a look of my entire example code here
This is because you are making a query from a database firestore, however the cloud support team has made it very cool to protect your applications from data leakages and so in a callable function as the name suggest you can only return data you passed to the same callable function through the data parameter and nothing else. if you try to access a database i suggest you use an onRequest Function and use the endpoint to get you data. that way you not only protect your database but avoid data and memory leakage.
examples of what you can return from a callable function
exports.sayHello = functions.https.onCall((data, context) => {
const name = data.name;
console.log(`hello ${name}`);
return `It was really fun working with you ${name}`;
});
first create a function in your index.js file and accept data through the data parameter but as i said you can only return data you passed through the data parameter.
now call the function
this is in the frontend code (attach an event listener to a button or something and trigger it
/* jsut say hello from firebase */
callButton.addEventListener('click', () => {
const sayHello = firebase.functions().httpsCallable('getAllUsers');
sayHello().then(resutls => {
console.log("users >>> ", resutls);
});
});
you can get your data using an onRequest like so
/* get users */
exports.getAllUsers = functions.https.onRequest((request, response) => {
cors(request, response, () => {
const data = admin.firestore().collection("users");
const users = [];
data.get().then((snapshot) => {
snapshot.docs.forEach((doc) => {
users.push(doc.data());
});
return response.status(200).send(users);
});
});
});
using a fetch() in your frontend code to get the response of the new onRequest function you can get the endpoint to the function in your firebase console dashboard.
but not that to hit the endpoint from your frontend code you need to add cors to your firebase cloud functions to allow access to the endpoint.
you can do that by just adding this line to the top of your index.js file of the firebase functions directory
const cors = require("cors")({origin: true});
We're trying to write a Google Cloud Function that gets a translation from Google Translate API, and then write the results to our Firebase Firestore database. Each works alone, but together nothing works. In other words, we can get a translation from Google Translate. We can write data to Firestore. But if we try to do both, we don't get a translation back from Google Translate, and nothing is written to Firebase. We get no error messages. We've tried the code with async await and with promises. Here's the code with promises:
exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => {
if (change.after.data().word != undefined) {
const {Translate} = require('#google-cloud/translate');
const projectId = 'myProject-cd99d';
const translate = new Translate({
projectId: projectId,
});
// The text to translate
const text = change.after.data().word;
// The target language
const target = 'en';
let translationArray = []; // clear translation array
translate.translate(text, target)
.then(results => {
translation = results[0];
translationArray.push(translation);
try {
// write translation to dictionary
admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
'translationArray': translationArray,
'language': 'en',
'longLanguage': 'English'
})
.then(function() {
console.log("Translation written");
})
.catch(function(error) {
console.error(error);
});
} catch (error) {
console.error(error);
}
})
.catch(error => {
console.error('ERROR:', error);
});
}
});
Here's the same code with async await:
exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => { // triggers when browser writes a request word to the database
if (change.after.data().word != undefined) {
async function getTranslation() {
try {
let translationArray = []; // clear translation array
const {Translate} = require('#google-cloud/translate');
const projectId = 'myProject-cd99d';
const translate = new Translate({
projectId: projectId,
});
// The text to translate
const text = change.after.data().word;
const options = {
to: 'en',
from: 'es',
format: 'text'
}
let [translations] = await translate.translate(text, options);
translations = Array.isArray(translations) ? translations : [translations]; // If translations is an array, leave it alone; if not, put it in an array
translationArray.push(translations[0]);
await admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
'translationArray': translationArray,
'language': 'en',
'longLanguage': 'English'
})
.then(function() {
console.log("Translation written");
})
.catch(function(error) {
console.error(error);
});
// };
} catch (error) {
console.error(error);
}
} // close getTranslation
getTranslation();
}
});
You're not returning a promise that's resolved when all the async work is complete. If you don't do that, Cloud Functions assumes that all your work is complete, and will clamp down on all resources, and any pending work will be shut down.
The promise returned by translate.translate().then().catch() is being ignored. Your nested call to admin.firestore()...set() has a similar problem. It's not sufficient to just call then() and catch() on every promise because then() and catch() both return yet another promise.
You're also unnecessarily mixing usage of try/catch with catch() on the promise. You don't need both strategies for error handling, just one or the other.
When you used await in your second example, you forced JavaScript to pause until the async work represented by the promise returned by set() was complete. This allowed your function to return only after all the work was finished, which is why it worked correctly.
You might be helped by watching my video series on use of promises and async/await in Cloud Functions. Proper handling of promises is crucial to creating a correctly working function.