How to call a Cloud Function for Firebase in Vue3 - javascript

I'm following the Net Ninja's tutorial about firebase cloud functions and I can't figure out how to call a function from a vue component. (he doesn't use vue in the tutorial).
So I have this vue3 app with firebase functions installed in it. How do I import the functions into a component? I can't find the answer anywhere. I'm still learning to code and I still don't understand very well how to import the packages needed.
I have a index.js file in the functions folder with a few functions inside.
I now want to call one of them ( a .onCall function) when clicking a button in a component.
I understand I need to import something in that component but I can't figure out what!

Your Vue.js app and your Cloud Functions for Firebase are totally different components of your whole application.
The Vue.js app is a front-end component (even if it is hosted in a cloud service like Firebase Hosting).
The Cloud Functions are serveless back-end components, hosted in the Firebase (Google Cloud) infrastructure and reacting to events.
To get these two components interacting with each other there are basically two possibilities:
For Callable Cloud Functions and HTTPS Cloud Functions, you will call them from your Vue.js app.
For background triggered Cloud Functions (e.g. triggered by a Firestore event like doc creation), the Vue.js front-end could generate the event (e.g. write to Firestore) and/or listen to the result of a Cloud Function execution (e.g. a Firestore doc is modified).
How do I import the functions into a component?
As explained above, you cannot integrate a Cloud Function code in your Vue.js code. In particular you cannot "import the functions into a component".
I now want to call one of them (a .onCall() function) when clicking a
button in a component.
If your Cloud Function is a Callable one, you can call it through the JS SDK as explained in the documentation.
UPDATE Following your comment:
As explained in the documentation, to call the Callable Function from your Vue.js app, you need to do as folloows (with the JS SDK v9):
Add Firebase to your Vue.js app. For example via a firebaseConfig.js file:
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getFunctions } from "firebase/functions";
const firebaseConfig = {
apiKey: "...",
// ....
};
const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
const functions = getFunctions(firebaseApp);
export { db, functions };
Then, in your component, you do
<script>
import { functions } from '../firebaseConfig';
import { httpsCallable } from 'firebase/functions';
// ...
methods: {
async callFunction() {
const addMessage = httpsCallable(functions, 'addMessage');
const result = await addMessage({ text: messageText })
const data = result.data;
//...
});
}
</script>

Related

Firebase problem "Uncaught (in promise) ReferenceError: exports is not defined"

I'm trying to create a new document when a user signs up for my app.
However, 'exports' is returning "Uncaught (in promise) ReferenceError: exports is not defined".
The code below is handling the function. I do also have an onAuthStateChanged function that switches some logged-in/out elements, although I don't think that could be stopping exports from being defined.
import { createUserWithEmailAndPassword, onAuthStateChanged,
signInWithEmailAndPassword } from "firebase/auth";
import { db, auth } from "./firebase";
import { collection, doc, setDoc, addDoc } from "firebase/firestore";
const signUpForm = document.querySelector('#signup-form');
if (signUpForm) {
signUpForm.addEventListener('submit', (e) => {
e.preventDefault();
//get user info
const email = signUpForm['signup-email'].value;
const password = signUpForm['signup-password'].value;
createUserWithEmailAndPassword(auth, email, password).then((cred) => {
const overlay = document.getElementById('overlay');
overlay.classList.add('hidden');
overlayP.classList.remove('hidden');
signUpForm.reset();
exports.createUserDoc = functions.auth.user().onCreate((user) => {
return admin.firestore().collection("users").doc(user.uid).setDoc({
email: user.email,
uid: user.uid,
})
});
// document.getElementById("signUpErr").innerHTML = "";
})
// .catch(err => {
// document.getElementById("signUpErr").innerHTML = err.message;
// });
});
};
I have initialized firebase and installed express.js within my index.js file but am I missing something to make sure this parameter is defined? I'm using Vite as a package bundler and node.js.
I'm new to coding and firebase, any advice would be massively appreciated.
I managed to solve this issue by taking a few days to read the documentation and understand what is going on with Cloud functions. The benefit of having a cloud function is that you can create triggers to your database that is away from your client-side code, improving security.
I was trying to call the cloud function within my app.js file and not within the firestore functions index.js file created when initializing firebase. Here you import through CommonJS Modules (CJS) the required SDK, in my case it was functions and admin.
Now my cloud functions live within index.js in /Functions folder separate from my app files.
// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
I also had to change the .setDoc() function to .set() as this was a Type error. I also added a userDelete function to delete users' documents in firestore.
exports.createUserDoc = functions.auth.user().onCreate(user => {
// Your new auth record will have the uid and email on it because
// you used email as the way to create the auth record
return admin.firestore().collection("users").doc(user.uid).set({
email: user.email,
bookmarked: []
})
});
exports.userDeleted = functions.auth.user().onDelete(user => {
const doc = admin.firestore().collection("users").doc(user.uid);
return doc.delete();
});
I would also like to note that setting up the firestore emulator has been very useful in this process and I'm sure will help me develop and test the other functions I need to create my app.

'No Firebase App '[DEFAULT]' has been created' even though initializeApp is called

I am trying to add Firebase (Firestore) to my Nuxt project, however I am recieving the following error when initialising a const from firebase.firestore() in my index.vue file:
Uncaught FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been
created - call Firebase App.initializeApp() (app/no-app).
I have installed Firebase in my project and also the module (#nuxtjs/firebase).
My nuxt.config.js file looks like this:
export default {
...
plugins: ['~/plugins/firebase.js'],
components: true,
buildModules: [
'#nuxt/typescript-build',
'#nuxtjs/tailwindcss',
],
modules: [],
...
}
And my firebase.js file is within my plugins folder as follows:
import firebase from 'firebase/app'
const config = {
...
}
let app = null
if (!firebase.apps.length) {
app = firebase.initializeApp(config)
}
export default firebase
I've compared the above to other examples online and haven't spotted any issues. However I'm new to everything from Nuxt to Firebase, so I may be missing something obvious. Any suggestions appreciated.
This typically happens if you call initializeApp() more than once on a single firebase app. If you're working with a single firebase db, make sure to initialize it when your app starts.
According to this GitHub Discussion this snippet for firebase.js should work:
import fb from "firebase/app"
export const firebase = !fb.apps.length ? fb.initializeApp(firebaseConfig) : fb.app()
// somecomponent.js
import {firebase} from "../firebase.js"
// do your firebase stuff here
Credit to #Brunocrosier with his post; even though this snippet isn't case-specific, I decided to include it for the sake of completeness.
Besides this thread - generally speaking, Uncaught FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app). is often a result of of either calling firebase. before initializing via .initializeApp(); or by calling .initializeApp() multiple times (for example Next.js might try to initialize it on the back- as well as the frontend - which seems to be the case in your code) within your firebase app.
Hence as a solution I highly suggest to move your initialization to the firebase.js file in order to initialize it directly when your app starts.
For further reading purposes:
The nuxt/firebase documentation
Error: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp()
The previously mentioned GitHub Discussion
FireBase JavaScript documentation
This normally happens when you try to access firestore before initializing the firebase. So as default we can check if firebase is initialized or not by firebase.apps.length but it's not a good practice to initialize firebase each and every time.
so if you are only using firestore then in your plugin you can export firestore directly after initialization like following,
import firebase from 'firebase/app'
const config = {
...
}
let app = null
if (!firebase.apps.length) {
app = firebase.initializeApp(config)
}
export default firebase.firestore()
But since you are working with nuxt there is special nuxt package called firebase/nuxt
with that installed you can define your configuration in nuxt config inside the module section as bellow,
modules: [
[
'#nuxtjs/firebase',
{
config: {
apiKey: '<apiKey>',
authDomain: '<authDomain>',
databaseURL: '<databaseURL>',
projectId: '<projectId>',
storageBucket: '<storageBucket>',
messagingSenderId: '<messagingSenderId>',
appId: '<appId>',
measurementId: '<measurementId>'
},
services: {
auth: true // Just as example. Can be any other service.
}
}
]
],
I think it is a better way to use firebase inside the nuxt.js

Check Firebase onAuthStateChanged before Vue app created in Nuxt JS

I have been using Vue JS v2 for some time now and I'm quite comfortable with it. Recently I have started using Nuxt JS and so far I was able to grasp most of the things. But after spending countless hours of searching the web, I am still unable to find out a way about how to use firebase onAuthStateChanged() before the Vue app gets created in Nuxt JS.
Let me explain a bit
In Vue 2 normally what I do is in main.js file
import Vue from 'vue'
import App from './App.vue'
import { auth } from '#/firebase/init'
let app = null
// ⚡ Wait for firebase auth to init before creating the Vue app
auth.onAuthStateChanged(() => {
// init app if not already created
if (!app) {
app = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
}
})
But in Nuxt JS the entire main.js file is encapsulated. Now I know that I can use plugin to do stuff like Vue.use() but again those things are happening after the Vue app is created.
I want to wrap the vue app creation inside the firebase auth checking so I see no way to implement this. So, if anyone has any idea about how to implement this then please let me know.
Please note that I am not trying to do any redirection based on the auth state. I know those answers already exist here and I have checked them already.
I had the same problem and found an easier solution. However, I don't why this is not documented anywhere in firebase nuxt...
In the layouts/default.vue Layout I implemented the following:
<template>
<div>
<div v-show="!isLoaded" class="loading">
</div>
<div class="app" id="app" v-if="loaded">
<NavBar :loggedIn="loggedIn" />
<Nuxt id="router-view" />
<Footer />
</div>
</div>
</template>
<script>
export default {
data() {
return {
loggedIn: false,
loaded: false
}
},
created() {
this.$fire.auth.onAuthStateChanged(() => {
this.loaded = true
const user = this.$fire.auth.currentUser;
if (user) {
this.loggedIn = true
}
})
},
...
}
</script>
I think this works exactly like using it in the main.js in vueJS. By using v-if the main app content should only be executed once firebase auth is initialised.
I guess I have a similar problem and I managed to create a login-flow, but it feels like a workaround.
on login I store cookie and user data:
after login via firenbase I save userdata from authUser in vuex store via the onAuthStateChanged hook, which resides in a clientside only plugin.
store firebase token as cookie
on pageReload:
init store on serverside with data from cookie, with nuxtServerInit:
https://github.com/davidroyer/nuxt-ssr-firebase-auth.v2/blob/master/store/index.js
onAuthStateChanged is triggered and does nothing, because the userStore is already initialised
on redirect into app:
if user comes back into my app from payment service provider for example, the vuex store is empty until firebaseAuth plugin loads and saves userdata in vuex store. I use localStorage to persist the public user here, so the app thinks we are still logged in. https://www.npmjs.com/package/nuxt-vuex-localstorage
In the background onAuthStateChanged is triggered. This feels wrong.
Especially in the "on redirect into app" case it would be nice if onAuthStateChanged is handled first, because then we could skip localStorage and use access userData from firestore fore example without having to wait for the authPlugin to come up.
Maybe a solution could be to write a module, which offers more hooks you can use:
https://nuxtjs.org/guide/modules/#run-tasks-on-specific-hooks

Firebase with angular odd solution

I'm trying to use firebase with angular.
I've stuck at one point when my application said firebase.initializeApp is not a function.
const firebase = require("firebase");
(it was this before: import * as firebase from "firebase"; )
So I've checked the firebase variable and inside the object there wasn't any initializeApp only another "firebase" object and a few other property.
in the firebase object there was finally the initializeApp.
So my solution was:
firebase.firebase.initializeApp({...});
I have tried other solutions and they didn't work.
I've declared these variables so I can use firebase as normally I wanted:
const FIREBASE_APP = require("firebase");
const FIREBASE = FIREBASE_APP.firebase;
Does anyone know a better solution for it?
Maybe there is some problem with the mapping in my config file?
(I can provide my system.config.js)
In your app.module.ts, use AngularFireModule from angularfire2
import {AngularFireModule} from 'angularfire2';
...
imports: {
AngularFireModule.initializeApp({...}),
}

How to unit test a function that depends on Firebase reference?

I want to unit test a function in my application that calls and updates a Firebase reference. The problem I am facing is that when I try to run the test and import the file that contains the function, I get the following error SyntaxError: Unexpected token u in JSON at position 0 which points to the line in my function file that imports my reference to Firebase.
I am using Webpack and Babel on this project, so I tried setting a resolve.alias in the webpack.config file which worked when the application was running, but did not work when I ran npm test. At this point I'm at a loss as to how I can mock out the Firebase reference so I can test the other functions of the function. Here's some sample code:
constants/index.js
import firebase from 'firebase';
const firebaseConfig = JSON.parse(unescape(`${config.FIREBASE_CONFIG}`));
const mainApp = firebase.initializeApp(firebaseConfig);
export const ref = mainApp.database().ref();
actions/settings.js
import * as actions from './';
import { ref } from '../constants';
export const updateSetting = (e, value) =>
(dispatch) => {
ref.child('setting')
.set(value)
.then(() => {
dispatch(actions.confirmFBSave());
})
.catch((err) => {
dispatch(actions.failFBSave({ text: err.code }));
});
};
What testing framework are you using Qunit, jasmine, mocha? With AngularFire and Angular it is much easier because you can pass a mock class in at DI, but I have also tested non angular js for server side. Then I used Sinon to get mock initializeApp and return a Mock firebase app.
You could also try https://github.com/thlorenz/proxyquire I didn't need to go that far, but I have seen it suggested. That way you are essentially intercepting the import and swap it out for something you can test with.

Categories

Resources