My Firebase Function runs in the emulator and writes to the same collection and document:
import * as functions from "firebase-functions";
export const MakeUppercase = functions.firestore.document('Messages/{docId}').onCreate((snap, context) => {
try {
const original = snap.data().original;
const uppercase = original.toUpperCase();
return snap.ref.set({ uppercase }, { merge: true });
} catch (error) {
console.error(error);
}
});
That works great. Now I want to write to a different collection in the Firestore emulator:
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";
const firebaseConfig = {
apiKey: "...",
authDomain: "triggerable-functions-project.firebaseapp.com",
projectId: "triggerable-functions-project",
storageBucket: "triggerable-functions-project.appspot.com",
messagingSenderId: "...",
appId: "..."
};
const app = initializeApp(firebaseConfig);
const db = getFirestore();
connectFirestoreEmulator(db, 'localhost', 8080);
export const MakeUppercase = functions.firestore.document('Messages/{docId}').onCreate((snap, context) => {
try {
const original = snap.data().original;
const uppercase = original.toUpperCase();
return db.firestore().collection('AnotherCollection').doc(context.params.docId).set({ uppercase }, { merge: true });
} catch (error) {
console.error(error);
}
});
That throws this error:
TypeError: db.firestore is not a function
I either have an incorrect Firestore DocumentReference or I didn't import a necessary module.
Let's write to Cloud Firestore. Same code, new module, different DocumentReference:
import * as admin from "firebase-admin";
...
return admin.firestore().collection('AnotherCollection').doc(context.params.docId).set({ uppercase }, { merge: true });
Same error:
TypeError: admin.firestore is not a function
Let's write to the Storage emulator:
import { getStorage, connectStorageEmulator, ref, uploadString } from "firebase/storage";
...
const storage = getStorage();
connectStorageEmulator(storage, "localhost", 9199);
...
return storage.collection('AnotherCollection').doc(context.params.docId).set({ uppercase }, { merge: true });
That throws the same error:
TypeError: storage.collection is not a function
Let's write to Cloud Storage:
const storageRef = ref(storage, 'Messages');
...
return uploadString(storageRef, uppercase);
The log says that the function executed without errors but when I go to Firebase Console I don't see the file in Cloud Storage.
Here's my final code. Why is app declared but never used?
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";
import { getStorage, connectStorageEmulator, ref, uploadString } from "firebase/storage";
import * as admin from "firebase-admin";
const firebaseConfig = {
apiKey: "...",
authDomain: "triggerable-functions-project.firebaseapp.com",
projectId: "triggerable-functions-project",
storageBucket: "triggerable-functions-project.appspot.com",
messagingSenderId: "...",
appId: "..."
};
const app = initializeApp(firebaseConfig);
const db = getFirestore();
connectFirestoreEmulator(db, 'localhost', 8080);
const storage = getStorage();
const storageRef = ref(storage, 'Messages');
connectStorageEmulator(storage, "localhost", 9199);
export const MakeUppercase = functions.firestore.document('Messages/{docId}').onCreate((snap, context) => {
try {
const original = snap.data().original;
const uppercase = original.toUpperCase();
return uploadString(storageRef, uppercase);
} catch (error) {
console.error(error); // emulator always throws an "unhandled error": "Your function timed out after ~60s."
}
});
You're using the new(ish) modular syntax of the v9 and later SDKs when you call getFirestore. With that new API, most calls are no longer namespaced, but are top-level functions. So there are no longer any public methods on the db object you have (as the error message says), but instead you have to pass the db object when calling those top-level functions.
The equivalent of this code:
db.firestore().collection('AnotherCollection').doc(context.params.docId).set({ uppercase }, { merge: true });
Would be:
setDoc(
doc(db, 'AnotherCollection', context.params.docId),
{ uppercase }, { merge: true })
)
Which is pretty close to the code sample in the documentation on setting a document with the v9 syntax, so I recommend keeping that handy while converting the rest of the code to the new API. The Upgrade from version 8 to the modular Web SDK guide is also a good place to get started, as are the blog posts Introducing the new Firebase JS SDK and The new Firebase JS SDK is now GA
Thanks, #Frank van Puffelen! Here's my Firebase version 9 code for writing to a different directory in the emulator:
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getFirestore, connectFirestoreEmulator, setDoc, doc } from "firebase/firestore";
const firebaseConfig = {
apiKey: "...",
authDomain: "triggerable-functions-project.firebaseapp.com",
projectId: "triggerable-functions-project",
storageBucket: "triggerable-functions-project.appspot.com",
messagingSenderId: "...",
appId: "..."
};
const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
connectFirestoreEmulator(db, 'localhost', 8080);
export const MakeUppercase = functions.firestore.document('Messages/{docId}').onCreate((snap, context) => {
try {
const original = snap.data().original;
const uppercase = original.toUpperCase();
return setDoc(
doc(db, 'AnotherCollection', context.params.docId),
{ uppercase }, { merge: true }
);
} catch (error) {
console.error(error);
}
});
Commenting out the emulator
connectFirestoreEmulator(db, 'localhost', 8080);
writes to Cloud Firestore. This threw an error:
Connection GRPC stream error. Code: 7 Message: 7 PERMISSION_DENIED: Missing or insufficient permissions.
I'm not going to worry about that, I presume that the emulator isn't intended to write to the Cloud.
As for Storage, my original code executes without errors but nothing writes to Storage in the emulator. Trying Cloud Storage, my Firebase Console refuses to set up Storage.
The documentation explains why I kept getting "not a function" errors. Instead of thinking of a Firestore location as a place with an address (in the namespace), in version 9 Firestore locations are functions with parameters.
While the version 8 APIs are based on a dot-chained namespace and
service pattern, version 9's modular approach means that your code
will be organized principally around functions.
David East explained in a blog post that the old (version 8) Firebase managed namespaces on the window but version 9 uses ES modules:
Historically libraries have been loaded and managed via a namespace on
the window, such as window.firebase. This technique does not allow for
tree shaking and lacks other benefits of the JavaScript module system.
I am trying to integrate firebase with ReactJs like this.
Here is my code
import firebase from "firebase";
var firebaseConfig = {
apiKey: "", // Add API Key
databaseURL: "" // Add databaseURL
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export const db = firebase;
var firepadRef = firebase.database().ref();
export const userName = localStorage.getItem('auth_name');
const urlparams = new URLSearchParams(window.location.search);
const roomId = urlparams.get("id");
if (roomId) {
firepadRef = firepadRef.child(roomId);
} else {
firepadRef = firepadRef.push();
}
export default firepadRef;
And now I get this warning:
It looks like you're using the development build of the Firebase JS
SDK. When deploying Firebase apps to production, it is advisable to
only import the individual SDK components you intend to use.
For the module builds, these are available in the following manner
(replace with the name of a component - i.e. auth, database,
etc):
CommonJS Modules: const firebase = require('firebase/app');
require('firebase/');
ES Modules: import firebase from 'firebase/app'; import
'firebase/';
Typescript: import firebase from 'firebase
After a new firebase.initializeApp, (assuming you are using v9 of firebase), you need to instantiate the db differently. Check your docs. I'd write your code like this instead:
// firebase file which you export the initialization
import firebase from "firebase";
var firebaseConfig = {
apiKey: "", // Add API Key
databaseURL: "" // Add databaseURL
};
// Initialize Firebase
var fire = firebase.initializeApp(firebaseConfig);
export fire.
//second js file
import firebase from ./firebase.js
import {getDatabase, ref, set} from "firebase/database";
var db = getDatabase(Firebase);
export const userName = localStorage.getItem('auth_name');
const urlparams = new URLSearchParams(window.location.search);
const roomId = urlparams.get("id");
//if you want to push to the db or something, you call the method
set(ref(db, 'users/' + userId), {
username: name,
email: email,
profile_picture : imageUrl
});
//more code here
All in all check the docs one more time. and use version 9, its implementation is the best.
https://firebase.google.com/docs/database/web/read-and-write#web-version-9_1
I'm getting the following CORS error when trying to invoke my callable function locally using the firebase cloud function emulator with my react app:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://us-central1-xxxx-xxxxx.cloudfunctions.net/getSignedUrls. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 302.
Here is my cloud function:
/functions/index.js
const functions = require('firebase-functions')
const AWS = require('aws-sdk')
const fs = require('fs')
const path = require('path')
const admin = require('firebase-admin')
admin.initializeApp()
exports.getSignedUrls = functions.https.onCall((data, context) => {
const cloudfrontAccessKeyId = "XXXXXXXXX"
let cloudfrontPrivateKey = fs.readFileSync(path.join(__dirname, 'private_key.pem'))
const signer = new AWS.CloudFront.Signer(cloudfrontAccessKeyId, cloudfrontPrivateKey)
const distName = "xxxxxxxxx.cloudfront.net"
const s3Path = data.path
let cfObjectUrl = 'https://' + distName + '/' + s3Path
const twoDays = 2*24*60*60*1000
const signedUrl = signer.getSignedUrl({
url: cfObjectUrl,
expires: Math.floor((Date.now() + twoDays)/1000)
})
return {url: signedUrl}
})
Here is where I create a reference to my apps cloud functions:
/src/firebase.js
import firebase from 'firebase/compat/app'
import { getAuth } from "firebase/auth";
import { getDatabase } from "firebase/database"
import {getFunctions} from 'firebase/functions'
const app = firebase.initializeApp({
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "xxxx-xxxx.firebaseapp.com",
databaseURL: "https://xxx-xxxx-default-rtdb.firebaseio.com",
projectId: "xxxx-xxxx",
storageBucket: "xxxx-xxxx.appspot.com",
messagingSenderId: "xxxxxxxxxxx",
appId: "1:938967916081:web:xxxxxxxxxxxxxxxxxxx",
measurementId: "G-xxxxxxxx"
})
export const auth = getAuth(app)
export const db = getDatabase(app)
export const functions = getFunctions(app)
export default app
And here is the component in which I'm attempting to call my cloud function:
/src/components/SignedUrlTest.js
import React, { Component } from 'react';
import {httpsCallable} from 'firebase/functions'
import {functions} from '../firebase'
class SignedUrlTest extends Component {
constructor(props){
super(props)
this.state = { url: null}
this.getUrl = this.getUrl.bind(this)
}
getUrl(){
var getSignedUrls = httpsCallable(functions, 'getSignedUrls');
getSignedUrls({path: "Melissa-OShaughnessy-6-1024x683.jpg"
}).then((result) => { console.log(result.data) })
.catch((error) => {
console.log(error)
})
}
render() {
return (
<div>
<button onClick={this.getUrl}>geturl</button>
</div>
);
}
}
export default SignedUrlTest;
I've poured over the docs (https://firebase.google.com/docs/functions/callable), but can't seem to figure out why I'm unable to call my function using the client SDK. I have tested a locally emulated 'onRequest' cloud function in the same environment and was able to get an appropriate response using my web browser, but I can't figure out how to invoke an 'onCall' function from my app.
Any help would be greatly appreciated :)
Did you try the solutions mentioned in this thread:
Enabling CORS in Cloud Functions for Firebase
Especially the one, going to the GCP console and setting the cloud function invoker setting to "allUsers".
I have a sitemap created with nuxt and firebase but when I deploy the site to firebase the sitemap throws and error since I am referencing my fireInit file but my nuxt.config.file has been moved to my firebase functions folder so I can deploy it. When I try to add the fireInit file in my firebase functions folder I then get an error because I am importing outside a module. Also when I take out my dynamic routes by commenting out my async routes function the site map doesn't show my custom domain url. It shows the https://us-central1-PROJECT_NAME.cloudfunctions.net.
File Structure
functions/
- nuxt.config (prod)
- index.js
public/
- _nuxt/ (client)
src/
- nuxt.config (dev)
firebase.json
Nuxt Config
module.exports = {
modules: ['#nuxtjs/sitemap'],
sitemap: {
hostname: process.env.BASE_URL,
exclude: ['/listings/create', '/listings/edit', '/profile/**'],
async routes() {
const { db } = require('./plugins/fireInit');
const snapshots = await db.collection('listings').get();
return snapshots.docs.map((doc) => `/listings/${doc.id}`);
},
},
}
./plugins/fireInit
import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseConfig = {
apiKey: 'KEY',
authDomain: 'KEY',
projectId: 'KEY',
appId: 'KEY',
measurementId: 'KEY',
};
// init firebase
!firebase.apps.length ? firebase.initializeApp(firebaseConfig) : '';
// init services
export const db = firebase.firestore();
You can't use the firebase web in functions and import it outside a module.
You have to read Firestore via firebase-admin on your index.js
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
And in your sitemap config:
sitemap: {
hostname: 'YOUR HOST',
exclude: ['/listings/create', '/listings/edit', '/profile/**'],
async routes() {
const snapshot = await admin.firestore().collection('listings').get()
return snapshot.docs.map((doc) => `/listings/${doc.id}`);
},
},
Iam using firebase admin but when I use exports like
var admin = require('firebase-admin');
var serviceAccount = require('./firebaseconfig.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://database.firebaseio.com"
});
module.exports = { admin : admin }
and used in another file by require like
var admin = require('../Firebaseconfig/firebase.js');
console.log(admin.database())
then gives error while starting the server
but if iam using admin.database() in the same file then Iam not getting any error.
error snippet:
console.log(admin.database())
TypeError: admin.database is not a function
at Object.
You exported an object containing admin. So you have to use admin.admin.database() :D
Or just export admin. module.exports = admin