Firebase callable functions, unsure about client code, getting internal error - firebase

I have been trying to get firebase functions to work for a day but failing miserably.
I have set up a http cloud function from the google cloud functions inline editor with code:
const admin = require('firebase-admin');
const functions = require('firebase-functions')
admin.initializeApp();
const db = admin.firestore();
exports.account_create_callable = functions.https.onCall((data,context) => {
var docRef = db.collection('users').doc(context.auth.uid)
docRef.set(
{
createdAt:Date.now(),
}, { merge: true }
);
return
})
I'm using React and there is very limited documentation but I've put together what I think is correct:
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
import "firebase/functions";
const firebase = firebase.initializeApp({
apiKey: process.env.REACT_APP_FB_API_KEY,
authDomain: process.env.REACT_APP_FB_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FB_PROJECT_ID,
});
const firestore = firebase.firestore();
// require('firebase-functions'); // I saw this is some documentation but don't think it's needed?
const functions = firebase.functions()
export function createUser(uid, data) {
const account_create_callable = functions().httpsCallable('account_create_callable')
account_create_callable()
}
However when I run this code, I get an internal error shown in the browser console.
Error: internal
at new HttpsErrorImpl (error.ts:65)
at _errorForResponse (error.ts:175)
at Service.<anonymous> (service.ts:276)
at step (tslib.es6.js:100)
at Object.next (tslib.es6.js:81)
at fulfilled (tslib.es6.js:71)
Interestingly, I get the same error when I call a non existent function like so:
functions().httpsCallable('asdf')
Would appreciate if someone could let me know where I'm going wrong.
My Firebase SDK is up to date.
I set up the function in the inline editor and set my region to eu-west 2, would this have any effect on my client code?

Since your Callable Function is deployed on europe-west2, you must initialize your client SDK with the appropriate region:
var functions = firebase.app().functions('europe-west2');
See https://firebase.google.com/docs/functions/locations#client-side_location_selection_for_callable_functions.

Related

Fetching from Firestore URL shows 404 - URL not right?

I am using Next.js to fetch data from my Firestore Database, but I keep getting an error in the console, stating that GET (FirestoreDatabaseURL) 404 (not found).
When I try any other json database such as myfakestore or jsonplaceholder, my code works (I tried both getServerSideProps and fetching with UseState), works beautifully. But not from my own database. Tried with Postman, but it won't work either.
I have tried to find different ways to get the database URL, but I am only finding this one format:
https://PROJECTID.firebaseio.com
The server is in us-central, which also helps determine the URL.
While testing around, I have gotten the error FetchError: invalid json response body at https://PROJECTID.firebaseio.com/ reason: Unexpected token F in JSON at position 0
Which I came to find out that it's not actually returning json, but HTML.
Just for context, this is my working code:
const [showProducts, setShowProducts] = useState()
const apiData = 'https://celeste-73695.firebaseio.com/'
let displayProducts
function pullJson () {
fetch(apiData)
.then(response => response.json())
.then(responseData => {
displayProducts = responseData.map(function(product){
return (
<p key={product.id}>{product.title}</p>
)
})
console.log(responseData)
setShowProducts(displayProducts)
})
//return
}
useEffect(() => {
pullJson()
},[])
And my firebase.js file
import firebase from 'firebase';
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "***",
authDomain: "***",
projectId: "***",
storageBucket: "***",
messagingSenderId: "***",
appId: "***",
measurementId: "***"
};
const app = !firebase.apps.length
? firebase.initializeApp(firebaseConfig)
: firebase.app();
const db = app.firestore();
export default db;
Can anybody point me in the right direction?
Thanks in advance.
The databaseURL property is for the Firebase Realtime Database, which you probably didn't create yet. The databaseURL property is not necessary to use Firestore though, so you should be able to access that with just the configuration data you have.
You may have created the realtime database but not have configured it with firebase config. I recommend you to go through this documentations for the realtime database.
To configure the firebase firestore you need to do the following:
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
// ...
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize Cloud Firestore and get a reference to the service
const db = getFirestore(app);
And make sure to export the db reference as will be used in your project.
After that you can start using the firestore like documented here as you have tried to use it with URL you may have to change the implementation for using it like shown in above documentations

How to do integration tests on Firebase Functions using local emulator?

I successfully wrote some tests for my Firebase functions, however now I want to test functions that manipulate Firestore data.
to do so I execute the following
export GOOGLE_APPLICATION_CREDENTIALS="my-project-key.json"
export FIRESTORE_EMULATOR_HOST="localhost:8080"
firebase emulators:start --import ./functions/test/fixture --project my-project
and then I run
npm run test
The test code is as follows:
const test = require("firebase-functions-test")()
const functions = require("../index")
describe("Tests", () => {
it("Do test", async () => {
const wrapped = test.wrap(functions.doTest)
const result = await wrapped({id:"1"})
})
})
The index file imported contains:
const functions = require("firebase-functions")
const admin = require("firebase-admin")
admin.initializeApp()
exports.doTest = functions.https.onCall(async (data) => {
const {id} = data
const vehicleRef = admin.firestore().collection("vehicles").doc(id)
const vehicle = await vehicleRef.get()
})
Yet every time I call a Firebase function that accesses Firestore I get the following error (in this case on vehicleRef.get()):
Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
at GoogleAuth.getApplicationDefaultAsync (/MyProject/firebase/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:157:19)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
at async GoogleAuth.getClient (/MyProject/firebase/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:490:17)
at async GrpcClient._getCredentials (/MyProject/firebase/functions/node_modules/google-gax/build/src/grpc.js:87:24)
at async GrpcClient.createStub (/MyProject/firebase/functions/node_modules/google-gax/build/src/grpc.js:212:23)
Caused by: Error
at Firestore.getAll (/MyProject/firebase/functions/node_modules/#google-cloud/firestore/build/src/index.js:784:23)
at DocumentReference.get (/MyProject/firebase/functions/node_modules/#google-cloud/firestore/build/src/reference.js:201:32)
at Function.run (/MyProject/firebase/functions/src/tracker.js:40:28)
at wrapped (/MyProject/firebase/functions/node_modules/firebase-functions-test/lib/main.js:72:30)
at Context.<anonymous> (/MyProject/firebase/functions/test/testTracker.js:42:26)
at callFn (/MyProject/firebase/functions/node_modules/mocha/lib/runnable.js:366:21)
at Test.Runnable.run (/MyProject/firebase/functions/node_modules/mocha/lib/runnable.js:354:5)
at Runner.runTest (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:677:10)
at /MyProject/firebase/functions/node_modules/mocha/lib/runner.js:801:12
at next (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:594:14)
at /MyProject/firebase/functions/node_modules/mocha/lib/runner.js:604:7
at next (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:486:14)
at Immediate._onImmediate (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:572:5)
What am I doing wrong?
Looks like you're doing everything right, except I don't think you're initializing the initializeApp object correctly. Try this in your index:
admin.initializeApp({
projectId: 'my-project',
credential: admin.credential.applicationDefault(),
});
You'll have to add a condition to use this version based on whether or not the environment variable is set though.

How to write to firestore emulator?

I am developing a firebase cloud function that writes to a firestore database.
During development I want the function to write to a local database. So I've started a firestore emulator. But the data is still written to the actual database.
How can I configure the cloud functions to use the local database?
This is my setup:
import * as functions from 'firebase-functions';
import * as cors from "cors";
import * as admin from "firebase-admin";
const REGION = "europe-west1";
const COLLECTION_CONTACT_FORM = "contact_form";
const serviceAccount = require("../keys/auth-key.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
const corsMiddleware = cors({origin: true});
export const sendContactForm = functions.region(REGION).https.onRequest((request, response) => corsMiddleware(request, response, async () => {
let {text} = request.body;
let result = await admin.firestore().collection(COLLECTION_CONTACT_FORM).add({text});
response.send((result.id));
}));
This is the console output when starting the emulator:
[1] i firestore: Serving WebChannel traffic on at http://localhost:8081
[1] i firestore: Emulator logging to firestore-debug.log
[1] ✔ functions: Emulator started at http://localhost:5000
[1] ✔ firestore: Emulator started at http://localhost:8080
[1] i functions: Watching "path/functions" for Cloud Functions...
[1] ⚠ functions: Your GOOGLE_APPLICATION_CREDENTIALS environment variable points to path/keys/auth-key.json. Non-emulated services will access production using these credentials. Be careful!
[1] ✔ functions[sendContactForm]: http function initialized (http://localhost:5000/project/europe-west1/sendContactForm).
When triggering the local endpoint, the production database is written to.
The firestore admin initializeApp() will correctly handle switching between local emulator and production database depending on where it is running. So if you simply remove the service account credentials it should work properly:
import * as functions from 'firebase-functions';
import * as cors from "cors";
import * as admin from "firebase-admin";
const REGION = "europe-west1";
const COLLECTION_CONTACT_FORM = "contact_form";
admin.initializeApp();
const corsMiddleware = cors({origin: true});
export const sendContactForm = functions.region(REGION).https.onRequest((request, response) => corsMiddleware(request, response, async () => {
let {text} = request.body;
let result = await admin.firestore().collection(COLLECTION_CONTACT_FORM).add({text});
response.send((result.id));
}));
But if for some reason you're trying to write to a firestore database outside of the one that the project is created in, you can use firestore/grpc separately from the firebase classes and then use the environment to either include your service account credentials or location emulator credentials. A local emulator example:
const {Firestore} = require('#google-cloud/firestore');
const {credentials} = require('#grpc/grpc-js');
const db = new Firestore({
projectId: 'my-project-id',
servicePath: 'localhost',
port: 5100,
sslCreds: credentials.createInsecure(),
customHeaders: {
"Authorization": "Bearer owner"
}
});
await db.collection("mycollection").doc("someid").set({ test: "value" });
Same answer, but with the docId set dynamically.
exports.makeUppercase = functions.firestore.document('Messages/{docId}').onCreate((snap, context) => {
const original = snap.data().original;
functions.logger.log('Uppercasing', context.params.docId, original);
const uppercase = original.toUpperCase();
// return snap.ref.set({ uppercase }, { merge: true });
return admin.firestore().collection('AnotherCollection').doc(context.params.docId).set({ uppercase }, { merge: true });
});
This grabs the docId that was set dynamically and uses it to write to a document with the same name but in a different collection.
Also I left in commented code for writing to the same document in the same collection. Beware that using onUpdate or onWrite instead of onCreate makes an infinite loop as each write triggers the function again!

Expo Snack: _firebase.default.firestore is not a function

I made a service in my React Native Expo Snack app that handles the instantiation of different firebase services. Somehow, Snack isn't able to find firestore, as it says "_firebase.default.firestore is not a function". The code used is shows below:
import firebase, { firestore } from 'firebase';
const config = { ... // Deleted, assume the config is correct }
export const FirebaseApp = !firebase.apps.length ? firebase.initializeApp(config) : firebase.app();
export const Firestore = firebase.firestore();
export const Auth = firebase.auth();
Can somebody confirm this should work in Snack? Why doesn't it recognize firestore() as a function?

react-boilerplate not working with firebase

I have imported firebase like this,
import * as firebase from 'firebase';
const config = {
};
firebase.initializeApp(config);
export const database = firebase.database().ref('/posts');
Then I got this error:
__WEBPACK_IMPORTED_MODULE_0_firebase_app__.database is not a function
so I tried this way.
const firebase = require('firebase/app');
require('firebase/auth');
require('firebase/database');
Then I got this error.
firebase.initializeApp is not a function
Does anyone know how to work with server side rendered react with webpack, firebase ?
Thanks.
instead of
export const database = firebase.database().ref('/posts')
try :
const database = firebase.database().ref('/posts')
module.exports = database

Resources