React with Cloud Firestore - javascript

Im trying to switch from realtime database to cloud firestore in my react app.
In my firebase.js, I have a definition of users as set out below.
class Firebase {
constructor() {
app.initializeApp(config).firestore();
this.auth = app.auth();
// this.db = app.firebase.database()
this.db = app.firestore();
}
doCreateUserWithEmailAndPassword = (email, password) =>
this.auth.createUserWithEmailAndPassword(email, password);
doSignInWithEmailAndPassword = (email, password) =>
this.auth.signInWithEmailAndPassword(email, password);
doSignOut = () =>
this.auth.signOut();
doPasswordReset = email =>
this.auth.sendPasswordResetEmail(email);
doPasswordUpdate = password =>
this.auth.currentUser.updatePassword(password);
// *** User API ***
user = uid => this.db.ref(`users/${uid}`);
users = () => this.db.ref('users');
}
This worked to get users when I was using realtime database, but now I get an error message that says:
this.db.ref is not a function
Why can't I reference this.db anymore and how do I navigate the cloud firestore docs to find the equivalent?
I have seen this post which recommends keeping the realtime database for some things. Is there a list of things that cloud firestore can't do and is getting a list of users one of them?
I have seen this documentation and tried:
user = uid => this.db.DocumentReference(`users/${uid}`);
users = () => this.db.DocumentReference('users');
This produces the same error message as using this.db.ref

There is no ref() in firestore, if you want to access a document you need to use the method document():
db.collection("users").add({
first: "Ada",
last: "Lovelace",
born: 1815
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
Please check the following doc for firestore :
https://firebase.google.com/docs/firestore/quickstart

Related

How to await the creation of a document created in cloud functions with onSnapshot

I want to securely create a user document onCreate that is in sync with the auth.user database in Firebase v9.
I think it wouldn't be secure to let a registered user create a user document. So I wrote a cloud function which triggers on functions.auth.user().onCreate() and creates a user document.
Problem:
I have the problem keeping them in sync as the onSnapshotmethod which should await for the user document to exists already returns a promise if the user document does not yet exists. Sometimes it works and sometimes not. So I don't know when I can update the by the cloud function created user document.
Question:
Why does the onSnapshot sometimes work and sometimes not. How can I fix it?
Here is a link to a helpful Article which seem to doesn't work in v9. Link
I tried and searched everywhere. I can't believe this is not a standard feature and still a requested topic. This seems so basic.
Error
error FirebaseError: No document to update: as const user = await createAccount(displayName, email, password); returns even if user is not yet in doc.data()
Sign Up function
interface SignUpFormValues {
email: string;
password: string;
confirm: string;
firstName: string;
lastName: string;
}
const createAccount = async (
displayName: string,
email: string,
password: string
) => {
// Create auth user
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
// -> Signed in
// Update Profile
const user = userCredential.user;
const uid = user.uid;
await updateProfile(user, {
displayName: displayName,
});
// IMPORTANT: Force refresh regardless of token expiration
// auth.currentUser.getIdToken(true); // -> will stop the onSnapshot function from resolving properly
// Build a reference to their per-user document
const userDocRef = doc(db, "users", uid);
return new Promise((resolve, reject) => {
const unsubscribe = onSnapshot(userDocRef, {
next: (doc) => {
unsubscribe();
console.log("doc", doc); // -> returning undefined
console.log("doc.data()", doc.data()); // -> returning undefined
resolve(user); // -> returning undefined
},
error: (error) => {
unsubscribe();
console.log("error", error);
reject(error);
},
});
});
};
const handleSignUp = async (values: SignUpFormValues) => {
const { firstName, lastName, email, password } = values;
const displayName = `${firstName} ${lastName}`;
try {
setError("");
setLoading(true);
// Create user account
const user = await createAccount(displayName, email, password);
console.log("createAccount -> return:", user); // -> problem here sometimes return undefined
// Update user
const newUserData = {
displayName: displayName,
firstName,
lastName,
};
// Build a reference to their per-user document
const userDocRef = doc(db, "users", user.uid);
await updateDoc(userDocRef, newUserData);
// Send Email verification
await authSendEmailVerification(user);
// Logout
await logout();
navigate("/sign-up/email-verification", { state: values });
} catch (error: any) {
const errorCode = error.code;
const errorMessage = error.message;
console.log("error", error);
console.log("error", errorCode);
if (errorCode === "auth/email-already-in-use") {
const errorMessage =
"Failed to create an account. E-Mail address is already registered.";
setError(errorMessage);
console.log("error", errorMessage);
} else {
setError("Failed to create account.");
}
}
setLoading(false);
};
Cloud function which triggers the user onCreate
// On auth user create
export const authUserWriteListener = functions.auth
.user()
.onCreate(async (user, context) => {
console.log("user:", user);
const userRef = db.doc(`users/${user.uid}`);
await userRef.set({
email: user.email,
createdAt: context.timestamp,
firstTimeLogin: true,
});
return db.doc("stats/users").update({
totalDocsCount: FieldValue.increment(1),
});
});
The issue is that the Cloud Function code runs asynchronously. There is no guarantee that it will run quickly enough to have the document created in Firestore between the end of createAccount() and your call to updateDoc(). In fact, if your system has been idle for a while it could be a minute (or more!) for the Cloud Function to execute (do a search for "cold start firebase cloud functions").
One option, depending on your design, might be to not take in first name and last name during sign up? But instead take the user to a "profile page" once they are logged in where they could modify aspects of their profile (by that time the user profile document hopefully is created). On that page, if the get() returns no document, you could put up a notification to the user that the system "is still processing their registration" or something like that.

Can't add user to firestore database after signing up with firebase

I want to add a user to a firestore database after signing up through firebase with email and password. I get no errors, and the user is created and is visible in firebase auth. However the user is not added to the firestore database.
Does anyone know how to make this work? My code snippet is pasted below.
firebase.initializeApp(config);
const auth = firebase.auth();
const db = firebase.firestore();
auth.createUserWithEmailAndPassword(email, pass).then(cred => {
const userId = cred.user.uid;
const userData = {
firstName: firstName,
lastName: lastName,
email: email
};
db.collection("users").doc(userId).set(userData).then(() => {
console.log("User successfully added to the DB!");
})
.catch((e) => {
console.log("Error adding user to the DB: ", e);
});
}).then(() => {
console.log('User created! - ' + email);
}).catch(e => {
console.log(e.message);
txtErrMsg.classList.remove('hide');
txtErrMsg.innerHTML = e.message;
});
FYI: I have console logged the parameters (userId and userData), and they appear just fine.
Thanks!
I figured out what was wrong with my code.
I wrote another method auth.onAuthStateChanged() which was interfering with my action to write to the database. Since I have two auth files (signup.js and login.js), I decided to only keep this method in login.js and to remove it from signup.js.
Now I can successfully add the user to the DB upon signup (signup.js), and I don't need to add anything to the DB when a user is simply logging in (login.js).
I hope this helps anyone out there experiencing the same problem.
I experienced that problem myself. The only way I managed to get this working was to await auth first, and then add the user to firestore:
I have a firebaseConfig.js where I set up the usersCollection:
import firebase from 'firebase';
import 'firebase/firestore';
const firebaseConfig = {
apiKey: ......
};
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const db = firebase.firestore();
const usersCollection = db.collection('users');
export { auth, usersCollection };
Then, I use it like this.
const fb = require('../firebaseConfig.js');
....
const cred = await fb.auth
.createUserWithEmailAndPassword(
'someemail#test.com',
'password123'
)
.catch(e => {
console.log(e.message);
});
if (cred) {
console.log({ cred });
const userId = cred.user.uid;
const userData = {
firstName: 'firstName',
lastName: 'lastName',
email: 'email',
};
fb.usersCollection
.doc(userId)
.set(userData)
.then(() => {
console.log('User successfully added to the DB!');
})
.then(() => {
console.log('User created!);
})
.catch(e => {
console.log('Error adding user to the DB: ', e);
});
}
When I ran this code I got the following in my log:
And it appears in my database like this:

firebase/firestore save user to collection

I am trying to save a user to a collection in my Firestore. It seems that the users are being created, I can see them in the Authentication tab, however they are not saving to my collection inside my firestore. There doesn't seem to be any errors inside my console either. I've been struggling with this for a couple hours now and i'm not even sure how to debug this.
export const authMethods = {
signup: (email, password, setErrors, setToken) => {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
// make res asynchronous so that we can make grab the token before saving it.
.then(async res => {
const token = await Object.entries(res.user)[5][1].b
// set token to localStorage
await localStorage.setItem('token', token)
// grab token from local storage and set to state.
setToken(window.localStorage.token)
const userUid = firebase.auth().currentUser.uid
const db = firebase.firestore()
db.collection('/users')
.doc(userUid)
.set({
email,
password,
})
console.log(res)
})
.catch(err => {
setErrors(prev => [...prev, err.message])
})
},
....
}
Any ideas?
Remove await from localStorage.setItem it isn't an asynchronous function.
You'll also need to add await to db.collection("/users").doc(userUid)
This is another approach that you could do. Let a cloud function handle that for you.
Whenever a user is created the following function is triggered.
export const onUserCreate = functions.auth
.user()
.onCreate(async (user, context) => {
await admin.firestore().collection("users").doc(user.uid).set({
id: user.uid,
emailAddress: user.email,
verified: user.emailVerified,
});
});
If you need more information about cloud functions read the following.
https://firebase.google.com/docs/functions/get-started

How to add a user to Firestore database after signing up through Firebase Authentication?

Upon sign up, I want to add a user to a "users" collection using the corresponding userID. Under this userID would be the user's data, such as name, email, posts, etc...
Here is my code:
I get no errors, and the user is created and is visible in firebase auth. However the user is not added to the firestore database.
firebase.initializeApp(config);
const auth = firebase.auth();
const db = firebase.firestore();
auth.createUserWithEmailAndPassword(email, pass).then(cred => {
const userId = cred.user.uid;
const userData = {
firstName: firstName,
lastName: lastName,
email: email
};
db.collection("users").doc(userId).set(userData).then(() => {
console.log("User successfully added to the DB!");
})
.catch((e) => {
console.log("Error adding user to the DB: ", e);
});
}).then(() => {
console.log('User created! - ' + email);
}).catch(e => {
console.log(e.message);
txtErrMsg.classList.remove('hide');
txtErrMsg.innerHTML = e.message;
});
It just seems like I am not able call the DB inside of the auth.createUserWithEmailAndPassword(email, pass) function. If I put the db.collection("users").doc(userId).set(userData) call with some dummy data outside of the auth.createUserWithEmailAndPassword(email, pass) function, then the data appears in the firestore DB.
FYI: I have console logged the parameters (userId and userData), and they appear as expected.

Error: Received unknown parameter: source

I'm making a react-redux app with firetore as database. Now, I wanted to use firebase cloud functions for handling stripe payments.
Here is the cloud function "createSubscription":
exports.createSubscription = functions.database
.ref("/teachers/{userId}/pro-membership/token")
.onWrite((event, context) => {
const tokenId = event.after.val();
const userId = context.params.userId;
if (!tokenId) throw new Error("Token Missing");
return admin
.database()
.ref(`teachers/${userId}`)
.once("value")
.then(snapshot => snapshot.val())
.then(user => {
console.log(user);
return stripe.subscriptions.create({
customer: user.customerId,
source: tokenId, **// Here is the error occuring**
items: [
{
plan: "pro-membership"
}
]
});
})
.then(sub => {
admin
.database()
.ref(`teachers/${userId}/pro-membership`)
.update({
status: "active"
});
})
.catch(err => {
console.log("ERRor", err);
});
});
Below is the error information from cloud function's logs:
source is not a valid parameter on a stripe.subscriptions.create request, see https://stripe.com/docs/api/subscriptions/create
Try updating the customer first, adding the token, https://stripe.com/docs/api/customers/update, then create a subscription!

Categories