Catching exceptions when calling set in Firestore - javascript

I haven't touch JS or Firebase for a while so forgive style issues. I ran into an issue calling set on a DocumentReference. You can't have a value of undefined. That's fine, but i expected my catch block to catch the exception, but instead the whole function crashed (this is within a cloud function). I've tried to simplify the code:
app.get('/test', (req, res) => {
return db.collection('users').doc('someid').set({
first: undefined
})
.then(()=> {
res.send('it worked?');
})
.catch((error: any) => {
console.error(error);
res.status(500).send('it didn\'t work');
})
})
A couple of notes, I'm using ExpressJS and testing this in the Firebase Functions emulator, writing to the real Firestore.
So my question is why isn't the error being caught? Instead I receive a response containing the exception details. Is the solution to check values before calling set? And if so, given it isn't written in the documentation linked above, how are you meant to know that?

The issue is that the set() function is throwing the exception before returning a promise because the parameter is invalid so you should validate that your object has no undefined fields.
From experimentation I have found that it accepts nulls so here is a way to work around the issue:
let firstValue;
app.get('/test', (req, res) => {
return db.collection('users').doc('someid').set({
first: firstValue || null
})
.then(()=> {
res.send('it worked?');
})
.catch((error: any) => {
console.error(error);
res.status(500).send('it didn\'t work');
})
})
I haven't found any indication in the documentation that the set() function does not accept data with undefined fields but I don't know why you would get them there in the first place as I don't think it would be useful to store it in the database. Also it is a best practice to always initialize variables to avoid having to deal with such issues, use blank values such as "" or {} if you haven't calculated the value yet.

Related

Wanted help in understanding how parameters are defined in Callback functions, using this example (if possible)

I am trying to understand more on how callback functions work in JavaScript.
I found this simple example code online and after adding the imports & additional txt file it's working. However I am not sure why it is working as the parameters of the functions aren't being defined anywhere?
fs.readFile(`${__dirname}/temp.txt`, (err, data) => {
console.log(`Breed: ${data}`);
superagent.get(`https://dog.ceo/api/breed/${data}/images/random`).end((err,res) =>{
console.log(res.body)
}); })
Upon running this I get the expected result on the console with the line displaying Breed: ${data} and the next line showing the body of the res.
However, the err, data parameters have never been provided to the arrow function within readFile. Neither has the err,res parameters ever been called/provided a value for it to display a message to the console, then how are these lines displaying a message to the console?
I could have understood this working if .readFile returned a err and data value & .end returned a err and res value, however upon reading the documentation both these functions return void, thus i'm confused and help would be appreciated.
You pass a function in to readFile, and later on readFile will call your function. When it calls your function, readFile will pass in the relevant values.
You can do something similar yourself, like this:
function myReadFile(filename, callback) {
setTimeout(() => {
if (Math.random() > 0.5) {
callback(null, 'fake file');
} else {
callback('an error', null);
}
}, 1000);
}
myReadFile(`${__dirname}/temp.txt`, (err, data) => {
console.log(`Breed: ${data}`);
})
(though of course the real readFile function isn't doing timeouts or random numbers, but rather reading from a file and listening for that operation to succeed or error out)

Why can't I catch error thrown from node-postgres?

I'm having an issue catching an error thrown from the Node-Postgres NPM package.
The issue seems simple on the surface, but I've tried everything I can think of.
My code is like the following:
import { Pool } from 'pg' // Import postgres connection pool
const pgPool = new Pool()
async function queryDatabase() {
try {
// Force TypeError by passing undefined
let queryResult = await pgPool.query( undefined )
if ( queryResult.rows.length > 0 ) {
return queryResult.rows[0]
}
return false
} catch( err ) {
// Never Reached
return new Error( 'Test error' )
}
}
queryDatabase()
And the error is as follows:
TypeError: Client was passed a null or undefined query
at Client.query (~/.../node_modules/pg/lib/client.js:479:11)
The error itself is pretty self-explanatory. I'm forcing the error here, for the sake of trying to handle it in the event that undefined gets passed by mistake. I realize that I can simply perform a check to make sure the input is never null or undefined, but that's not my main concern.
My worry is if I can't catch this error thrown from this package, how many other unforeseen cases am I going to encounter where I simply can't catch and handle a thrown error.
I've tried numerous different approaches - The Async/Await Try/Catch method, shown above - I've tried pgPool.query().then().catch() - Combinations of the two. I've even tried running the catch against the Pool instance itself. No matter what I do, I can't handle the exception without using Node's process.on('unhandledRejection', ...), which is of course a bad idea.
I've been racking my brain on this for hours. Is there any way that I can catch and handle errors like this, so it's not crashing my server every time? Thanks in advance!
I was able to reproduce this and it seems to be an actual bug in the pg-library.
According to the source if you call .query on a pool instance, this instance will attempt to connect and get a client. In this connect-callback the actual query is dispatched to the client-module, which will throw the mentioned type error if the query is nil.
This error is thrown synchronously (i.e. the error is not passed to the callback argument, e.g. callback(new TypeError("...")) and since there's no try/catch around the client.query call in the pool's connect-callback, the error will not be caught by your try/catch.
A potential fix would be to wrap the client.query call in a try catch:
client.once('error', onError)
this.log('dispatching query')
try {
client.query(text, values, (err, res) => {
this.log('query dispatched')
client.removeListener('error', onError)
if (clientReleased) {
return
}
clientReleased = true
client.release(err)
if (err) {
return cb(err)
} else {
return cb(undefined, res)
}
})
}catch(err) {
return cb(err)
}
So for now, you probably should create an issue on github and wait for the bugfix or fork the repo and use above workaround, I'm afraid.

What is the correct way to check errors with firebase auth methods?

I recently started using firebase. I am creating a simple app containing email password authentication. When the user submits the form there are two possibilities.
If there is any error then Form will show error will remain visible.
If there is no error Form will hide and remove error.
The loader needs to be hide after processing in either case. The relevant part of the code is.
let error = false;
auth
.createUserWithEmailAndPassword(email, password)
.catch(err => {
if (err) error = true;
RegisterForm.setError(err.message);
})
.then(x => {
if (!error) {
RegisterFormDialog.close();
}
})
.finally(() => {
MainLoader.hide();
});
The above code is working completely fine but the problem is that I have to create an extra variable error is the outer scope and then check it inside then(). Is there any callback what will only run if there are no errors. Because then() is called even when errors are there.
The usual practice for all methods that return a promise (not just Firebase) is to put then before catch when handling the results:
let error = false;
auth
.createUserWithEmailAndPassword(email, password)
.then(x => {
RegisterFormDialog.close();
})
.catch(err => {
if (err) error = true;
RegisterForm.setError(err.message);
})
.finally(() => {
MainLoader.hide();
});
If the promise resolves successfully, the then callback will be invoked. Otherwise, the catch callback will be invoked. But never both.

what is dot for in front of .catch? (firebase auth, react native)

I am very new to react native with somewhat of a background in C(EE courses) and is just trying to understand the concepts. I'm currently folloing a react native course on Udemy.
I am trying to create a login form using firebase + react native.
Here I would like to notify the user if an error occurs with their login.
I have two sets of code below in which I attempt to do the same thing, one of them works and the other one doesn't. I would like to understand why the first one works, and why the second one doesn't
This does work:
firebase.auth().createUserWithEmailAndPassword(email, password)
.catch((error) => {
this.setState({ error: error.message, loading: false });
});
Why do I need to put error on the left of the arrow function? From my understanding, whatever is on the left side of the arrow function can be seen as the "input", and the rightside is the system/output?
This doesn't work:
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(this.createFailure.bind(this))
createFailure() {
.catch((error) => {
this.setState({ error: error.message, loading: false });
});
}
This one gives me a parse error for the '.' in front of catch.
I don't think I quite understand how .catch works but I was only able to find catch() on mozilla without the '.'
it seems that I lack some fundamental understanding of how certain elements work, are there any recommended Youtube series that explains these building blocks? I find documentations often have too many corner cases which makes everything quite confusing.
createUserWithEmailAndPassword returns a promise object. That object has a catch method, which you use to hook up a handler for when the promise rejects. So the . is just like the . in, say, $("#foo").html(): It's accessing the method on the object returned by the function.
Why do I need to put error on the left of the arrow function? From my understanding, whatever is on the left side of the arrow function can be seen as the "input", and the rightside is the system/output?
Pretty much, yes. The arrow function is called if the promise rejects. When it gets called, it receives the error as input, and then does something with that error.
This asynchronous code using promises:
doSomething()
.then(data => {
// ...do something with data...
})
.catch(error => {
// ...do something with error...
});
is the same, logically, as this synchronous code:
try {
const data = doSomething();
// ...do something with data
} catch (error) {
// ...do something with error
}
and in fact, if you use an async function, you can write the asynchronous version using promises almost exactly like that:
// (within an `async` function)
try {
const data = await doSomething();
// Note -----^^^^^^
// ...do something with data
} catch (error) {
// ...do something with error
}
Re your code that doesn't work, if I assume you want to call createFailure when an error occurs, you probably want this (see comments):
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(result => {
// This is the success path
this.setState({/*...from result...*/});
}
.catch(error => {
// This is the error path
this.createFailure(error);
});
or within an async function:
// (within an `async` function)
try {
const result = await firebase.auth().createUserWithEmailAndPassword(email, password);
// This is the success path
this.setState({/*...from result...*/});
} catch (error) {
// This is the error path
this.createFailure(error);
}

Firebase Firestore: when do promises from offline write operations resolve?

I activated offline as stated in the docs like:
firebase
.firestore()
.enablePersistence()
.then(() => {
console.log('offlinemode acctivated')
})
The log appears as I would expect.
When adding data like so:
db
.collection('foo')
.add({foo: 'bar'})
.then(docRef => {
console.log('Added Foo: ', docRef.id)
// do some stuff here with the newly created foo and it's id.
})
.catch(console.error)
Neither .then() nor .catch() are getting called while offline. This is even though the object is added to the foo collection in my offline DB as this callback is executed:
db
.collection('foo')
.onSnapshot(callback)
Am I missing something? I would expect the promise to either fail or resolve, so I can react accordingly.
Promises from write operations in Firestore will only resolve when there is confirmation from the server that the write completed, even though they may successfully be written to local cache.
Here's my solution:
I wrap the call in a function that should eventually return a resolving promise no matter the offline/online status
I then get the saved doc from onSnapshot which returns the doc written to local cache (works both online and offline).
Here's my code (with a little typescript):
export function dbWritePromise(functionPromise: Promise<any>): Promise<any>{
if(window.navigator.onLine){
return functionPromise
}
else{
return Promise.resolve()
}
}
// I grabbed this function from a Github issue one upon a time
export function docSnapshotPromise(ref: firebase.firestore.DocumentReference): Promise<any>{
return new Promise((resolve, reject) => {
const unsubscribe = ref.onSnapshot(doc => {
resolve(doc)
unsubscribe()
}, err => {
reject(err)
unsubscribe()
})
})
}
In use (I'm using the update function here, but add would work the same way) This code is working with documents from a collection called organizations:
try{
//update org doc
await dbWritePromise(orgRef.update({
name: 'New and improved name here'
}))
// wait for this updated doc to be written to local cache, then we can get the updated org
const updatedOrgRef = await docSnapshotPromise(orgRef)
const updatedOrg = updatedOrgRef.data()
console.log(updatedOrg.name) // outputs the new and improved name
}
catch (err) { handleError(err) }
The error thrown might be some error with local cache, or it might be a server error such as a permissions error returned by the Firestore rules (when online). Obviously any error from the server during offline mode would silently fail, even when the app is back online.
I'd love to see other peoples' solutions here!

Categories

Resources