Unhandled Rejection (FirebaseError): No document to update - node.js

I'm still very new to coding so bear with me! I have followed a youtube course to build a note app and get a base to work from, but I'm now getting this error at random times when deleting the notes in firebase, hoping someone might be able to spot what's cooking here!
"Unhandled Rejection (FirebaseError): No document to update: projects/speakle-dc94b/databases/(default)/documents/notes/GdWPrQNxR3Z9TFMWmqOZ"
And it's referencing the node modules like so:
screenshot of the error in chrome
The code I have that interacts with firebase looks like this:
componentDidMount = () => {
firebase
.firestore()
.collection('notes')
.onSnapshot(serverUpdate => {
const notes = serverUpdate.docs.map(_doc => {
const data = _doc.data();
data['id'] = _doc.id;
return data;
});
console.log(notes);
this.setState({ notes: notes });
});
}
selectNote = (note, index) => this.setState({ selectedNoteIndex: index, selectedNote: note });
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.update({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
newNote = async (title) => {
const note = {
title: title,
body: ''
};
const newFromDB = await firebase
.firestore()
.collection('notes')
.add({
title: note.title,
body: note.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
const newID = newFromDB.id;
await this.setState({ notes: [...this.state.notes, note] });
const newNoteIndex = this.state.notes.indexOf(this.state.notes.filter(_note => _note.id === newID)[0]);
this.setState({ selectedNote: this.state.notes[newNoteIndex], selectedNoteIndex: newNoteIndex });
}
deleteNote = async (note) => {
const noteIndex = this.state.notes.indexOf(note);
await this.setState({ notes: this.state.notes.filter(_note => _note !== note) })
if(this.state.selectedNoteIndex === noteIndex) {
this.setState({ selectedNoteIndex: null, selectedNote: null});
} else {
this.state.notes.lenght > 1 ?
this.selectNote(this.state.notes[this.state.selectedNoteIndex - 1], this.state.selectedNoteIndex - 1) :
this.setState({ selectedNoteIndex: null, selectedNote: null });
}
firebase
.firestore()
.collection('notes')
.doc(note.id)
.delete()
.then(function() {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
}
}

It simply means that there is no document of that name to be uploaded.
you could either use set() or add() to add the document because it is not present.
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.update({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
replace the above code with this
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.add({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}
or
noteUpdate = (id, noteObj) => {
firebase
.firestore()
.collection('notes')
.doc(id)
.set({
title: noteObj.title,
body: noteObj.body,
timestamp: firebase.firestore.FieldValue.serverTimestamp()
});
}

I was working with something like this only especially in Cloud Functions and while writing an endpoint for doing some operation I came across the below-quoted error.
I was trying to read a document in a collection and if it existed I was trying to write a new doc into another collection. So it was kind of a nested code.
A piece of my code.
const firstDocRef = db.collection('myFirstCollection').doc('myDocName');
const existDoc = firstDocRef.get()
.then((resDoc)=>{
if(resDoc.exists)
{
db.collection('mySecondCollection').add({
.
.
.
.
.
orderCreatedAt:Firestore.FieldValue.serverTimestamp()
})
.then((new_doc)=>{
return res.status(200);
// return 200 ok what we were trying to achieve has completed.
})
.catch(()=>{
console.log("Log, as write failed somehow");
return res.status(500);
// return a 500 internal server error occurred
});
}
else
{
console.log("My first condition wasn't met, so logged it");
return res.end();
// properly terminate the processing of request
}
});
/*.catch((err)=>{
console.log("Our first doc presence check couldn't complete and hence I arrived here, log it");
res.writeHead(500);
return res.end();
// again give back 500 to client
});*/
UnhandledPromiseRejectionWarning: ReferenceError: Firestore is not defined
UnhandledPromiseRejectionWarning: Unhandled promise rejection.
This error originated either by throwing inside of an async function without a catch block
Now I am also new to Firebase but I came across this and somehow solved it.
So I was not getting the above error if I was putting in a catch block in get() document.
Strange huh!
Removed the catch block by commenting it. Got this error.
Now, this is a haywire error, it says the catch is not there, but we did it on purpose.
So I began searching, came across this question here on stack overflow and saw it's still unanswered. Searched and read the documentation myself.
I would like to tell you that this isn't because of any Firestore Security Rules, or anything else. Because I came across some guesses around these notions too while searching for an answer.
The common thing we all are doing here is that we are trying to achieve the ServerTimeStamp at FireStore
I would like to bring your notice to my imports in my node cloud function code.
const functions = require('firebase-functions');
const express = require('express');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
So you see, I am using the new way of getting the permission to use Firestore because I am trying to establish a cloud function.
Now this is the documentation reference provided by Google: Click here!
The right syntax proposed by above API reference is
Firestore.FieldValue.serverTimestamp()
It is the culprit, it isn't providing me any timestamp and if there is not a catch block unhandled promise error is occuring and no error is being shown while debugging, it just doesn't work.
I did this, Solution part:
Even after those imports in my Node Program, I imported this:
const {Firestore} = require('#google-cloud/firestore');
Now all I did was that I used the statement in the timestamp field as
Firestore.FieldValue.serverTimestamp()
Exactly as mentioned and even used a catch block just in case any other problem occurs while at production. That is using db constant to do all the Database Transactional Stuff and just for serverTimeStamp I have to bring in new import.
And it worked, I guess require('#google-cloud/firestore') statement imported as {FireStore} brings in all the things that are required for the FieldValue thing to use as a reference.
I hope it helps any new person looking for it and saves a lot of time which I wasted searching for a solution.
I have verified it by running the cloud function on firebase emulator.

You could simply do it like this
You
Get it
If it exsits: you update it
If it doesn't exist, you set it.
const docRef = this.db.collection("users_holdings").doc(userId);
docRef.get().subscribe((doc) => {
if (doc.exists) {
docRef.update({
stockHoldings: stockHoldings,
});
} else {
docRef.set({
stockHoldings: stockHoldings,
});
}
});

Related

Query is not working with promise for Dynamo DB

I have a dynamo db table where I was able to insert data using node js via lambda. I am able to query from the console and I am also able to query using the cli. When using query with promise its erroring out with invoke error. Its not throwing any specific errors. IF I remove promise and run I can see that connection is successful to the db. I also tried ExpressionAttributeValues: {
":name": {"S":id}
},
even hard coding the value for id and same issue. What am I doing wrong??
import AWS from "aws-sdk"
const dyanamoDB = new AWS.DynamoDB.DocumentClient()
AWS.config.update({ region: "us-east-1" })
export const checkIFIDExist = async (id) => {
try {
const params = {
ProjectionExpression: "String1, String2",
IndexName: "String2",
KeyConditionExpression: "String2 = :name",
ExpressionAttributeValues: {
":name": id
},
TableName: 'my-table',
}
const data = await dynamoDB.query(params).promise()
console.log("Data:", data)
return "success"
}catch (err) {
throw new Error (`Failed query for ${id} `, err)
}
}
Error:
2022-08-16T20:24:09.210Z c2e0c093-2719-48b8-b0bb-4f38de3ac7b6 ERROR Invoke Error
{
"errorType": "Error",
"errorMessage": "Failed query for OE0K0I ",
"stack": [
"Error: Failed query for OE0K0I ",
" at checkIFStepFunctionIDExists (file:///var/task/src/dynamo-query.js:24:15)",
" at processTicksAndRejections (internal/process/task_queues.js:95:5)",
" at async Runtime.handler (file:///var/task/src/index.js:11:19)"
]
}
I basically deleted and trashed the project created new one and did the same stuff I had mentioned earlier in my post and instead of throwing error after catch statement console log it and I am getting the result I expected. I really couldn't tell what was I doing wrong before. #jarmond the error I posted above, I accidentally included a dependency while changing something and caused the error I provided. Thanks everyone for looking into the issue.
If the promise() function doesn't do what you expect it to do. It's worth noting, that you can actually also do the same thing with the standard Node.js promisify function.
import { DocumentClient } from "aws-sdk/clients/dynamodb";
import { promisify } from "util";
const docClient = new AWS.DynamoDB.DocumentClient()
...
const data = await promisify((cb) => docClient.query(params, cb))();
As #jarmod pointed out, there's probably something else going on though. I added some sidenotes to clarify some things that you may or may not already know.
Some sidenotes
Here are just some remarks which aren't entirely on topic but which can lead to confusion.
// don't do this, it doesn't do what you think it does.
throw new Error(`Failed query for ${id}`, err );
// this prints both a text and an object.
console.error(`Failed query for ${id}`, err);
// this throws just an error with a message
throw new Error(`Failed query for ${id}`);
// there is a 2nd argument which you can pass, which is an "options" parameter, which you can use to send a `cause` along.
throw new Error(`Failed query for ${id}`, { cause: err } );
PS:More details about it can be found it in the MDN documentation.
I haven't seen how you got it working without the promise, but if you did it like this, then it's not what you think.
try {
const params = { ... };
dynamoDB.query(params);
// this point is always reached
return "success"
}catch (err) {
// this point is never reached
}
Instead, without a promise, you would have to use a callback function.
const params = { ... };
dynamoDB.query(params, (err, data) => {
if(err) {
console.error(err);
return;
}
console.log("success", data);
});

TypeError: Cannot read property 'name' of undefined at Firestore.snapshot_

I am using firebase for my cloud functions in my flutter app.
For some reason, my cloud function is not working. This is the console error:
TypeError: Cannot read property 'name' of undefined at Firestore.snapshot_ (/workspace/node_modules/#google-cloud/firestore/build/src/index.js:709:128) at beforeSnapshotConstructor (/workspace/node_modules/firebase-functions/lib/providers/firestore.js:146:30) at changeConstructor (/workspace/node_modules/firebase-functions/lib/providers/firestore.js:150:49) at cloudFunction (/workspace/node_modules/firebase-functions/lib/cloud-functions.js:134:34)
This is my cloud function code:
exports.onSendingNotificationsReceived3 = functions.firestore.document('/orders/{documentId}')
.onCreate(async (snap, context) => {
// Grab the current value of what was written to Firestore
snap.data()['items'].forEach((result,index) =>{
functions.logger.log('Token loop entered');
//Getting list of shopIds from the items list in order
if(listOfFCMToks.includes(result['fcmToken'])==true){
functions.logger.log('Token already exists');
}else{
listOfFCMToks.push(result['fcmToken']);
functions.logger.log('Token of shop is added');
}
});
/* snap.data()['shipperTokens'].forEach((result,index) =>{
functions.logger.log('ShipperToken loop entered');
//Getting list of shopIds from the items list in order
if(listOfFCMToks.includes(result)==true){
functions.logger.log('Token already exists');
}else{
listOfFCMToks.push(result);
functions.logger.log('ShipperToken loop entered'+index);
}
});*/
functions.logger.log('3');/*
const shopRef = admin.firestore().collection("shops");
const querySnapshot = await shopRef.get();
querySnapshot.forEach(async (doc) => {
functions.logger.log('4');
listOfShopIds.forEach(async (item) =>{
if(item==doc.data()['sid']){
functions.logger.log('55');
allChecksids.push(doc.data()['sid']);
const tokenRef = admin.firestore().collection("users").doc(doc.data()['sid']).collection('tokens').doc('fcmToken');
const tokenCurrent = await tokenRef.get();
functions.logger.log('6');
functions.logger.log('88');
listOfUserTokensForFCM.push(doc.data()['token']);
listOfUserTokensForFCM.push('DataIsFound');
}
});
});*/
functions.logger.log('66');
await admin.messaging().sendMulticast({
tokens : listOfFCMToks,
notification: {
title: "A NewOrder is Placed",
body: "Please check your dashboard in Telloo App",
imageUrl: "https://firebasestorTage.googleapis.com/v0/b/learning-firebase-2d636.appspot.com/o/newOrder.jpg?alt=media&token=71ad7cc8-6be6-4069-b097-75cc4f437978g",
},
}) .then((response) => {
console.log(
"Notifications are sent"
);});
return snap.ref.set({listOfFCMToks}, {merge: true});
});
Each order contains different items from multiple shop. I'm accessing fcmToken and trying to save them in a list in the same order with the cloud function use this code:
snap.data()['items'].forEach()
The function was working properly. But now I'm facing the error given above in the console.
To check if this isn't the problem with the syntax of function, I created another function and tested it:
exports.getOrderId = functions.firestore
.document('/orders/{documentId}')
.onCreate((snap, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = snap.data();
// access a particular field as you would any JS property
const name = newValue.orderId;
functions.logger.log($name);
// perform desired operations ...
});
But upon testing this function, I'm getting the same error:
getOrderIdxtsp49f33azi ReferenceError: $name is not defined at exports.getOrderId.functions.firestore.document.onCreate (/workspace/index.js:147:60) at cloudFunction (/workspace/node_modules/firebase-functions/lib/cloud-functions.js:135:23) at Promise.resolve.then (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/function_wrappers.js:140:25) at process._tickCallback (internal/process/next_tick.js:68:7)
What solution I found from internet:
Soution: Change node version to 8.
But from now we can't use functions under node 10.
Help!

node.js firestore delete with a where clause

I've tried to figure this out but don't know enough about node.js or firestore to make it happen. I want to delete a bunch of documents based on a where clause. I have tried a bunch of iterations of this but none of them delete, and using the Google provided code returns an async error:
db.collection("testcollection")
.where("office", "==", 12345).limit(3).get().then((snapshot)=> {
snapshot.docs.forEach(doc => {
//const res = await db.collection('testcollection').doc(id).delete();
const data = doc.data();
console.log(doc.id);
delete(doc.id);
})
});```
Well shoot. Right after posting this I figured it out by using an answer from here Cloud Firestore delete function
and putting doc.id into a variable:
snapshot.docs.forEach(doc => {
const tempid = doc.id;
db.collection("testcollection").doc(tempid).delete().then(function() {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
})
});

Call external API with Cloud Function and create document

I'm trying to create some cloud functions to:
call Google Directions API using Axios
create a document at Firestore based on the API result
Return the document reference to my iOS App.
(I'm on Blaze plan, pay as you go)
I'm having trouble to create the following functions as my Node / JS knowledge is very basic.
Could someone please have a quick a look and let me know what I'm missing?
Obs.:
The code is deploying to firebase with no warnings and erros. I'm pretty sure that the problem is the way that I'm trying to return my callbacks.
Thanks in advance
EDIT
I've made a few changes on the code, but I'm still receiving nil on my iOS App.
The code is still not creating a document on firestore.
const functions = require('firebase-functions');
const axios = require('axios');
var admin = require("firebase-admin");
admin.initializeApp();
// Func called by iOS App, If user is auth, call google maps api and use response to create a document at firestore
exports.getDistanceAndSavePackage = functions.https.onCall((data, context) => {
if (!context.auth){ return {status: 'error', code: 401, message: 'Not signed in'} }
const userId = context.auth.uid;
const startCoordinates = data.startCoords;
const endCoordinates = data.endCoords;
const pkgDocReference = getGoogleRoute(startCoordinates, endCoordinates, res => {
console.log('google cloud function has returned');
let venueId = userId;
let distance = res.distance.value;
let resultStartAdd = res.start_address;
let resultEndAdd = res.end_address;
const pkgDocRef = createTempPackage(venueId, distance, resultStartAdd, resultEndAdd, resultPkg => {
return resultPkg
})
return pkgDocRef;
})
return pkgDocReference;
});
//Create Package Document
function createTempPackage(venueId, distance, startingAddress, endingAddress, callback){
console.log('Creating temp package');
const docRef = admin.firestore().doc(`/temp_packages/`)
docRef.set({
id: docRef.id,
venue_id: venueId,
distance: distance,
starting_address: startingAddress,
ending_address: endingAddress,
timestamp: admin.database.ServerValue.TIMESTAMP,
status: 0
})
.then(docRef => {
console.log('Doc created')
return callback(docRef);
}).catch(error => {
console.log('Error trying to create document')
return callback(error);
})
}
//Call Google directions API
function getGoogleRoute(startCoords, endCoords, callback){
axios({
method: 'GET',
url: 'https://maps.googleapis.com/maps/api/directions/json',
params: {
origin: startCoords,
destination: endCoords,
key: 'mykey'
},
})
.then(response => {
let legs = response.data.routes[0].legs[0];
return callback(legs);
})
.catch(error => {
console.log('Failed calling directions API');
return callback(new Error("Error getting google directions"))
})
}
I don't know if this is final solution, however there is an error in the code:
const docRef = admin.firestore().doc('/temp_packages/')
This statement should trow error:
Value for argument "documentPath" must point to a document, but was "${documentPath}". Your path does not contain an even number of components.
The error is thrown before docRef.set so it will not be taken into consideration in catch statement. I was trying to test it, but all my tries finished with this error. Maybe this error is somewhere in your logs.
I hope it will help!
For a HTTPS trigger you'd need to return {status: 'OK', code: 200, data: json}.
So that it would actually respond through the web-server.

Using Firebase cloud functions callable

I am trying to use the admin sdk to check for a user phone number. when i check for a number in the database, it displays the result, but when i input a number not in the database, it throws an internal error.
below is a sample code of the functions index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.checkPhoneNumber = functions.https.onCall(async (data, context) => {
const phoneNumber = await admin.auth().getUserByPhoneNumber(data.phoneNumber);
return phoneNumber;
})
front-end.js
toPress = () => {
const getNumberInText = '+12321123232';
const checkPhone = Firebase.functions.httpsCallable('checkPhoneNumber');
checkPhone({ phoneNumber: getNumberInText }).then((result) => {
console.log(result);
}).catch((error) => {
console.log(error);
});
}
below is the error i am getting when i input a number thats not in the auth
- node_modules\#firebase\functions\dist\index.cjs.js:59:32 in HttpsErrorImpl
- node_modules\#firebase\functions\dist\index.cjs.js:155:30 in _errorForResponse
- ... 14 more stack frames from framework internals
As you will read in the documentation of Callable Cloud Functions:
The client receives an error if the server threw an error or if the resulting promise was rejected.
If the error returned by the function is of type function.https.HttpsError, then the client receives the error code, message, and details from the server error. Otherwise, the error contains the message INTERNAL and the code INTERNAL.
Since you don't specifically manage errors in your Callable Cloud Function, you receive an INTERNAL error.
So, if you want to get more details in your front-end, you need to handle the errors in your Cloud Function, as explained here in the doc.
For example, you could modify it as follows:
exports.checkPhoneNumber = functions.https.onCall(async (data, context) => {
try {
const phoneNumber = await admin.auth().getUserByPhoneNumber(data.phoneNumber);
return phoneNumber;
} catch (error) {
console.log(error.code);
if (error.code === 'auth/invalid-phone-number') {
throw new functions.https.HttpsError('not-found', 'No user found for this phone number');
}
}
})
We throw an error of type not-found (See all the possible Firebase Functions status codes here) in case the error code returned by the getUserByPhoneNumber() method is auth/invalid-phone-number (See all the possible error codes here).
You could refine this error handling code, by treating other errors returned by getUserByPhoneNumber() and sending to the client other specific status codes.
Here is a way I usually use to check if a field (ex. phone) exists in any of the documents in my collection.
For an example based on what you described here is a collection I have created:
The code to make a query for checking if a phone exists looks like this: (I'm using Node.Js)
let collref = db.collection('posts');
var phoneToCheck = '+123456789'
const phone1 = collref.where('phone', '==', phoneToCheck)
let query1 = phone1.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
If a Document exists with that phone number the response is like this:
I no document has that phone number then the response is the following:

Resources