Unable to create a new collection or document Firestore - firebase

I have been trying to connect my web app to Firebase in order to save some input data into a Firestore database. I used Firebase a few months ago and everything was fine but now when I try to link it to a new project I'm unable to do anything. From the console, when I start a new collection, I can't save it and if I do it directly from my web app nothing is happening and then I get an error message :
#firebase/firestore: Firestore (9.8.4): Connection WebChannel transport errored
I saw such question on here but the only solutions given were either a service outage or that after a few refresh it might work but I have been trying for a few days now and I'm still unable to do anything
Don't know if it is very relevant as the issue is also in the firebase console but here's my firebase config
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
//
};
export const app = initializeApp(firebaseConfig);
export const database = getFirestore(app);
const collectionRef = collection(database, "userID");
const handleClick = () => {
addDoc(collectionRef, { title })
.then(() => console.log("Data Added"))
.catch((err) => console.log(err.message));
};

Related

RESTful API hosting on GCP

I have this multi layered application entirely hosted on GCP. At the moment, we only have the back-end part. Front-end and API are to be developed. For the front-end, the decision has been made - it will be a React.js app hosted on Firebase Hosting and the authentication method will be Email/password and users will be provisioned manually through the Firebase Hosting UI.
As we'd like to have a re-usable middle layer (API) we're in a process of making a decision what type of a solution to be used for our middle layer. The main request here is only logged in users to be able to call the API endpoints. Eventually, there will be also a native/mobile application which will have to also be able to make authenticated requests to the API.
My question here is, what type of GCP service is advised to pick here? I want it to be light, scalable and price optimized. Preferred programming language would be C# but Node.js would be also acceptable.
Firebase Functions would work well for this authenticated API. With a function, you can simply check for the existence of context.auth.uid before proceeding with the API call.
https://firebase.google.com/docs/functions/callable
You'll want to use the .onCall() method to access this context.auth object.
Here's an example I took from one of my active Firebase projects which uses this concept:
Inside your functions>src folder, create a new function doAuthenticatedThing.ts
/**
* A Firebase Function that can be called from your React Firebase client UI
*/
import * as functions from 'firebase-functions';
import { initializeApp } from 'firebase/app';
import { connectFirestoreEmulator, getFirestore, getDocs, query, where, collection } from 'firebase/firestore';
import firebaseConfig from './firebase-config.json';
let isEmulator = false;
const doAuthenticatedThing = functions
.region('us-west1')
.runWith({
enforceAppCheck: true,
memory: '256MB',
})
.https.onCall(async (_data, context) => {
// disable if you don't use app-check verify (you probably should)
if (context.app == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called from an App Check verified app.',
);
}
// checks for a firebase authenticated frontend user
if (context.auth == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The user must be authenticated.',
);
}
// establish firestore db for queries
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
// start the emulator
if (process.env.MODE === 'development' && !isEmulator) {
connectFirestoreEmulator(db, '127.0.0.1', 6060);
isEmulator = true;
}
// obtain the user's firebase auth UID
const uuid = context?.auth?.uid as string;
// do some database stuff
const ref = collection(db, 'collection-name');
const q = query(ref, where(uuid, '==', uuid));
const results = await getDocs(q);
if (results.empty) {
throw new functions.https.HttpsError(
'internal',
'There were no results found!',
);
}
// prepare document data
const data: Array<any> = [];
// gather chats, and an array of all chat uids
results.forEach((d) => {
data.push({ id: d.id, data: d.data() });
});
return data;
});
export default doAuthenticatedThing;
Make sure to reference this new Firebase Function in the functions/src/index.ts file.
import doAuthenticatedThingFn from './doAuthenticatedThing';
export const doAuthenticatedThing = doAuthenticatedThingFn;
Create a frontend React Hook so any component can use any function you make. Call it useGetFunction.ts
import { getApp } from 'firebase/app';
import { getFunctions, HttpsCallable, httpsCallable } from '#firebase/functions';
const useGetFunction = (functionName: string): HttpsCallable<unknown, unknown> => {
const app = getApp();
const region = 'us-west1';
const functions = getFunctions(app, region);
return httpsCallable(functions, functionName);
};
export default useGetFunction;
Now you can simply get this function and use it in any React component:
const SomeComponent = () => {
const doAuthenticatedThing = useGetFunction('doAuthenticatedThing');
useEffect(() => {
(async () => {
const results = await doAuthenticatedThing();
})();
}, []);
};

How to make Firestore query during SSR

I would like to fetch data from Firestore during server side rendering. I know I could use REST API (and attach the token to the request's headers) but I don't want to write REST requests on server side and then duplicate the same requests on client side using standard Firestore queries. On client I prefer standard queries (no REST) because of the realtime updates. And I would like to reuse the queries from client also on the server (even without the benefit of realtime updates).
I validate the token manually on the server:
import admin from 'firebase-admin';
import { initializeApp, getApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
const firebaseApp = initializeApp(config);
const db = getFirestore(firebaseApp);
const decodedIdToken = await admin.auth().verifySessionCookie(sessionCookie);
// => token verified: decodedIdToken.userId = "xxx"
But when I want to perform a query:
import { collection, getDocs } from 'firebase/firestore';
const querySnapshot = await getDocs(collection(db, 'myCollection'));
I get error:
{
"code": "permission-denied",
"name": "FirebaseError"
}
Firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
On client side the error could be solved using onAuthStateChanged but I can't use this listener on server.
Is there any way to run Firestore queries with manually verified token?
I've managed to replicate your error. You're getting this error because you're trying to use client SDK instead of firebase-admin.
Here's the sample code for your reference:
import admin from 'firebase-admin';
import { initializeApp } from 'firebase-admin/app';
import { getFirestore } from "firebase-admin/firestore";
const firebaseApp = initializeApp(config);
const db = getFirestore(firebaseApp);
// const decodedIdToken = await admin.auth().verifySessionCookie(sessionCookie);
// => token verified: decodedIdToken.userId = "xxx"
// const querySnapshot = await getDocs(collection(db, 'myCollection'));
db.collection("myCollection").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
});
I've used version 8 (commonJS) instead of version 9 (modular) as firebase-admin still uses the dot notation syntax.
[sampleQuery] => { test: 'testing' }
Here's the link on how to get all documents in a collection.
Here's another reference on upgrading to Node.js SDK Admin SDK v10 (modular SDK).
Update:
If you wanted to use the Firestore Security Rules, you need to use custom signed tokens, you need to pass it to signInWithCustomToken so that the client auth can sign in.
Below is a sample code for your reference:
import { getAuth, signInWithCustomToken } from "firebase/auth";
const auth = getAuth();
signInWithCustomToken(auth, token)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ...
});
A new user will be created and linked all their credentials and the new account will be stored as part of your project, and will be used to identify a user across every app in your project.
You can also allow a user to sign out by calling signOut:
import { getAuth, signOut } from "firebase/auth";
const auth = getAuth();
signOut(auth).then(() => {
// Sign-out successful.
}).catch((error) => {
// An error happened.
});
You can check this documentation on authenticating with Firebase for additional information.

Firebase JavaScript Code Complete/Intellisense

I started to develop Firebase apps and was using VSCode. I followed some on line tutorials but was unable to get code completion/intellisense working in vscode.
There seems to be nothing on the internet on this. I found a few posts but nothing work.
Such as this one: Code completion for Firebase in VS code?
Here is a sample of my code, querying a collection of document:
import { initializeApp } from 'firebase/app';
document.addEventListener("DOMContentLoaded", (evt)=> {
const app = firebase.app()
console.log(app)
const db = firebase.firestore()
const myToDoThing = db.collection("thingstodo").doc("firstthing")
//get the document
myToDoThing.get()
.then(doc=>{
const data = doc.data()
console.log(data)
//console.log(data.createAt )
})
//Get live update of the document
myToDoThing.onSnapshot(doc=>{
const data = doc.data()
console.log(data)
})
})

Firebase Cloud Function not executing Flutter

I have the following function in my index.ts file:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
const fcm = admin.messaging();
export const sendToDevice = functions.firestore
.document('orders/{orderId}')
.onCreate(async snapshot => {
print("aa")
console.log("osakosak");
const order = snapshot.data();
const querySnapshot = await db
.collection('users')
.doc(order.ustaID)
.collection('tokens')
.get();
const tokens = querySnapshot.docs.map(snap => snap.id);
const payload: admin.messaging.MessagingPayload = {
notification: {
title: 'New Order!',
body: `you sold a ${order.day} for ${order.time}`,
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToDevice(tokens, payload);
});
However, when the new document gets added into the order collection, this doesn't get triggered. Even the print and console.log don't work. I tried putting print and console log before export, and it still didn't fire.
Based on your comments ("It depends on cloud_firestore in pubspec.yaml"), it seems that you didn't deploy your Cloud Function correctly.
As a matter of fact, Cloud Functions are totally independent from your Flutter app (your front-end). It is a back-end service. You should deploy it with the Firebase CLI, see the doc. Note that the code shall be in the Firebase Project, not in your Flutter project.

React Native - upload image to Firebase using #react-native-firebase/storage - No Firebase App '[DEFAULT]' has been created

I am getting the following error when uploading an image to Firebase:
Error: No Firebase App '[DEFAULT]' has been created - call firebase.initializeApp()
Here is my code:
App.js
import * as Firebase from 'firebase';
componentDidMount() {
Firebase.initializeApp(firebaseConfig);
}
Profile.js
import * as Firebase from 'firebase';
import rnFb from '#react-native-firebase/storage';
uploadImage = localUri =>
new Promise((resolve, reject) => {
const localUri2 = Platform.OS === 'ios' ? localUri.replace('file://', '') : localUri;
const fbUri = Firebase.storage().ref();
rnFb().ref(localUri2).putFile(fbUri)
.then(
() => { resolve(); }
)
.catch(
(e) => { reject(e); }
);
});
It's failing at the .putFile line.
I don't understand what the problem is because I am calling .initializeApp() in App.js
UPDATE 12/21
I added console.log(Firebase.apps.length); right before rnFb().ref(localUri2).putFile(fbUri) and the output is 1...very strange indeed.
...and if I do exactly as the error asks and call firebase.initializeApp() right before rnFb().ref(localUri2).putFile(fbUri) I get the error Error:
Firebase: Firebase App named '[DEFAULT]' already exists
Help!!
My understanding is that the Firebase SDK used internally inside #react-native-firebase is independent of the ordinary Firebase SDK from firebase.
It can be exposed using:
import firebase from '#react-native-firebase/app';
// OR
import { firebase } from '#react-native-firebase/storage';
Applying these changes (and simplifying your code), leaves you with the following:
import storage, { firebase } from '#react-native-firebase/storage';
// can possibly be somewhere else
firebase.initializeApp(firebaseConfig);
uploadImage = localUri => {
const localUri2 = Platform.OS === 'ios' ? localUri.replace('file://', '') : localUri;
return storage().ref('/path/to/upload/to').putFile(localUri2)
}
Rather than use "client initialization" using firebase.initializeApp(), you can also use "native initialization"
for Android and iOS.
Have you followed the API documentation? I can't seem to find anywhere in the docs that you need to manually call initializeApp()
I think your fbUri is wrong. The param has to be a string.
For ex:
firebase
.storage()
.ref('remote_path') // remote path where you want to store
.putFile(
'local/ok.jpeg' // local file
)
.then(successCb)
.catch(failureCb);

Resources