Firebase SDK V9 auto-configuration with /__/firebase/init.js - firebase

After moving to the modular SDK V9 auto-configuration does not work any more using /__/firebase/init.js. It's expected as init.js contains the old initialization schema.
Unfortunately it is not mentioned neither in the documentation nor in the migration guide how to make auto-configuration work in the V9.

You can fetch /__/firebase/init.json at runtime but then you have to deal with initializeApp(firebaseConfig) being a promise or top-level await which isn't well supported in tooling yet.
import { initializeApp } from 'firebase/app';
const response = await fetch('/__/firebase/init.json');
const firebaseApp = initializeApp(await response.json());
It's a hack but you could also work around this with /__/firebase/init.js.
<script>
// Capture the config object from /__/firebase/init.js
window.firebase = {
initializeApp: (config) => {
window.firebaseConfig = config;
// Delete fake Firebase global
delete window.firebase;
},
};
</script>
<script src="/__/firebase/init.js"></script>
<script type="module">
import { initializeApp } from 'firebase/app';
const firebaseConfig = window.firebaseConfig;
if (!firebaseConfig) {
throw new Error('window.firebaseConfig is not defined');
}
const firebaseApp = initializeApp(firebaseConfig);
</script>

For now, the V9 SDKs only work with the explicit configuration snippet (which you can get from the Firebase console and from your site's __/firebase/init.json).
Unfortunately, since the __/firebase/init.js and __/firebase/init.json is provided by Firebase Hosting at run-time, it's not really compatible with tree-shaking (which is the primary reason for the modular SDKs).

Related

Uncaught ReferenceError: firebase is not defined

I tried several methods but nothing works.
I link this:
<script src="https://www.gstatic.com/firebasejs/ui/6.0.1/firebase-ui-auth.js"></script>
<link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/6.0.1/firebase-ui-auth.css" />
It's config to firebase:
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-analytics.js";
import { getFirestore } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-firestore.js";
const firebaseConfig = {
// some config
};
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
const db = getFirestore(app);
async function getCities(db) {
const citiesCol = collection(db, 'cities');
const citySnapshot = await getDocs(citiesCol);
const cityList = citySnapshot.docs.map(doc => doc.data());
return cityList;
}
</script>
Config to auth:
<script>
var uiConfig = {
signInSuccessUrl: 'localhost:5500',
signInOptions: [
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
signInMethod: firebase.auth.EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD
}
],
tosUrl: 'localhost:5500',
privacyPolicyUrl: function() {
window.location.assign('localhost:5500');
}
};
var ui = new firebaseui.auth.AuthUI(firebase.auth());
ui.start('#firebaseui-auth-container', uiConfig);
</script>
Problem is Uncaught ReferenceError: firebase is not defined.
For:
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
I'm trying to connect authorization through firebase to my website.
How to fix this? Help please.
The "Uncaught ReferenceError: firebase is not defined" error occurs when the code is trying to use the firebase object, but the object is not defined. This can happen for a number of reasons, including:
The Firebase JavaScript library has not been properly loaded. Make sure that you have included the correct script tag to load the Firebase library in your HTML file:
<script src="https://www.gstatic.com/firebasejs/7.21.0/firebase-app.js"></script>
You might be trying to use the firebase object before the library has finished loading. To fix this, you can wrap your code in a function and call it after the firebase library has finished loading:
window.onload = function() {
// Your code here
};
There may be a typo or other mistake in the name of the firebase object. Make sure that you are using the correct spelling and case when referring to the object.
If you are using a bundler like webpack or rollup, make sure that the Firebase library is correctly imported and available to your code.

Firebase auth CDN not exporting module

I am following the official Google tutorial example to include Firebase via CDN instead of the SDK:
https://firebase.google.com/docs/web/alt-setup#from-the-cdn
After copying the example verbatim:
<body>
<!-- Insert this script at the bottom of the HTML, but before you use any Firebase services -->
<script type="module">
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.4.1/firebase-app.js'
// If you enabled Analytics in your project, add the Firebase SDK for Google Analytics
import { analytics } from 'https://www.gstatic.com/firebasejs/9.4.1/firebase-analytics.js'
// Add Firebase products that you want to use
import { auth } from 'https://www.gstatic.com/firebasejs/9.4.1/firebase-auth.js'
import { firestore } from 'https://www.gstatic.com/firebasejs/9.4.1/firebase-firestore.js'
</script>
</body>
i get the browser error:
Uncaught SyntaxError: The requested module
'https://www.gstatic.com/firebasejs/9.4.1/firebase-auth.js' does not
provide an export named 'auth'
Outcommenting the auth import line and you get the exact same error with the firestore import line instead.
Is this a problem with the files / tutorial or on my end?
Seems the tutorial is referring to the wrong files?
Looks the code sample added is wrong. Please try using the below code to use version 9+ SDK for web
Visit https://firebase.google.com/docs/auth/web/password-auth#sign_in_a_user_with_an_email_address_and_password
import { getAuth, signInWithEmailAndPassword } from 'https://www.gstatic.com/firebasejs/9.5.0/firebase-auth.js'
const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
})
.catch((error) => {`enter code here`
const errorCode = error.code;
const errorMessage = error.message;
});

How to import Firebase only on client in Sapper?

I'm importing Firebase into my Sapper application, I do not want the imports to be evaluated on the server. How do I make sure imports are only on the client-side?
I am using Sapper to run sapper export which generates the static files. I have tried:
Creating the firebase instance in it's own file and exported the firebase.auth() and firebase.firestore() modules.
Trying to adjust the rollup.config.js to resolve the dependencies differently, as suggested from the error message below. This brings more headaches.
Creating the Firebase instance in client.js. Unsuccessful.
Creating the instance in stores.js. Unsuccessful.
Declaring the variable and assigning it in onMount(). This causes me to have to work in different block scopes. And feels a bit hacky.
The initialization of the app, works fine:
import firebase from 'firebase/app'
const config = {...}
firebase.initializeApp(config);
I have also discovered that if I change the import to just import firebase from 'firebase' I do not get this server error:
#firebase/app:
Warning: This is a browser-targeted Firebase bundle but it appears it is being run in a Node environment. If running in a Node environment, make sure you are using the bundle specified by the "main" field in package.json.
If you are using Webpack, you can specify "main" as the first item in
"resolve.mainFields": https://webpack.js.org/configuration/resolve/#resolvemainfields
If using Rollup, use the rollup-plugin-node-resolve plugin and set "module" to false and "main" to true: https://github.com/rollup/rollup-plugin-node-resolve
I expected to just export these firebase functionalities from a file and import them into my components like:
<script>
import { auth } from "../firebase";
</script>
But as soon as that import is include, the dev server crashes. I don't want to use it on the server, since I'm just generating the static files.
Does anyone have some ideas on how to achieve importing only on client side?
So I have spent too much time on this. There isn't really a more elegant solution than onMOunt.
However, I did realize that sapper really should be used for it's SSR capabilities. And I wrote an article about how to get set up on Firebase with Sapper SSR and Cloud Functions:
https://dev.to/eckhardtd/how-to-host-a-sapper-js-ssr-app-on-firebase-hmb
Another solution to original question is to put the Firebase CDN's in the global scope via the src/template.html file.
<body>
<!-- The application will be rendered inside this element,
because `app/client.js` references it -->
<div id='sapper'>%sapper.html%</div>
<!-- Sapper creates a <script> tag containing `app/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
<!-- Insert these scripts at the bottom of the HTML, but before you use any Firebase services -->
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-app.js"></script>
<!-- Add Firebase products that you want to use -->
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.0.4/firebase-firestore.js"></script>
</body>
</html>
and in the component:
<script>
import { onMount } from 'svelte';
let database, authentication;
onMount(() => {
database = firebase.firestore();
authentication = firebase.auth();
});
const authHandler = () => {
if (process.browser) {
authentication
.createUserWithEmailAndPassword()
.catch(e => console.error(e));
}
}
</script>
<button on:click={authHandler}>Sign up</button>
I was able to import firebase using ES6. If you are using rollup you need to consfigure namedExports in commonjs plugin:
//--- rollup.config.js ---
...
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/idb/build/idb.js': ['openDb'],
'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
},
}),
The you can use it like this:
//--- db.js ---
import * as firebase from 'firebase';
import 'firebase/database';
import { firebaseConfig } from '../config'; //<-- Firebase initialization config json
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export { firebase };
// Initialize db
export const db = firebase.firestore();
and maybe use it in a service like such:
// --- userService.js ----
import { db } from './common';
const usersCol = db.collection('users');
export default {
async login(username, password) {
const userDoc = await usersCol.doc(username).get();
const user = userDoc.data();
if (user && user.password === password) {
return user;
}
return null;
},
};
EDITED
Full rollup config
/* eslint-disable global-require */
import resolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import commonjs from 'rollup-plugin-commonjs';
import svelte from 'rollup-plugin-svelte';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
import config from 'sapper/config/rollup';
import { sass } from 'svelte-preprocess-sass';
import pkg from './package.json';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
// eslint-disable-next-line no-shadow
const onwarn = (warning, onwarn) =>
(warning.code === 'CIRCULAR_DEPENDENCY' && warning.message.includes('/#sapper/')) || onwarn(warning);
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
svelte({
dev,
hydratable: true,
emitCss: true,
preprocess: {
style: sass(),
},
}),
resolve({
browser: true,
}),
commonjs({
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/idb/build/idb.js': ['openDb'],
'node_modules/firebase/dist/index.cjs.js': ['initializeApp', 'firestore'],
},
}),
legacy &&
babel({
extensions: ['.js', '.mjs', '.html', '.svelte'],
runtimeHelpers: true,
exclude: ['node_modules/#babel/**'],
presets: [
[
'#babel/preset-env',
{
targets: '> 0.25%, not dead',
},
],
],
plugins: [
'#babel/plugin-syntax-dynamic-import',
[
'#babel/plugin-transform-runtime',
{
useESModules: true,
},
],
],
}),
!dev &&
terser({
module: true,
}),
],
onwarn,
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
svelte({
generate: 'ssr',
dev,
}),
resolve(),
commonjs(),
],
external: Object.keys(pkg.dependencies).concat(require('module').builtinModules || Object.keys(process.binding('natives'))),
onwarn,
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode),
}),
commonjs(),
!dev && terser(),
],
onwarn,
},
};
The clean way is to use the Dynamic Import as the documentation said: Making a component SSR compatible
The way to get around this is to use a dynamic import for your component, from within the onMount function (which is only called on the client), so that your import code is never called on the server.
So here for example we want to import the core of firebase and the authentication package too.
<script>
let firebase;
onMount(async () => {
const module = await import("firebase/app");
await import("firebase/auth");
firebase = module.default;
firebase.initializeApp(firebaseConfig);
});
<script>
And now you can use firebase object as you can, for example we want to login with email and password:
let email;
let password;
async function login() {
try {
let result = await firebase.auth().signInWithEmailAndPassword(
email,
password
);
console.log(result.user);
} catch (error) {
console.log(error.code, error.message);
}
}
In order to use Firebase with Sapper, you have to import firebase not firebase/app. You do want firebase to be able to load correctly with SSR on the backend, not just the frontend. If you have some metatags, for example, that would be stored in the database, you want them to load on the backend (UNTESTED).
You could just use firebase, but then you get the annoying console warning. Remember also firebase loads ALL firebase dependencies while firebase/app does not, that is why you don't want to use it on the frontend. There is probably a way with admin-firebase, but we want to have less dependencies.
Do not use rxfire at all. You don't need it. It causes errors with Sapper. Just plain Firebase.
firebase.ts
import firebase from 'firebase/app';
import "firebase/auth";
import "firebase/firestore";
import * as config from "./config.json";
const fb = (process as any).browser ? firebase : require('firebase');
fb.initializeApp(config);
export const auth = fb.auth();
export const googleProvider = new fb.auth.GoogleAuthProvider();
export const db = fb.firestore();
Firebase functions require an extra step and you must enable dynamic imports. (UNTESTED)
export const functions = (process as any).browser ? async () => {
await import("firebase/functions");
return fb.functions()
} : fb.functions();
While this compiles, I have not tried to run httpsCallable or confirmed it will load from the database on the backend for seo ssr from the db. Let me know if it works.
I suspect all of this will work with the new SvelteKit now that Sapper is dead.

Firebase initialization error in VueJS Project

first time posting here. Also quite new to VueJS, know some Angular.
I ran into an error when initializing Firebase(first time getting this error) in my VueJS project:
That showed up briefly after click event, so I had to slow down my network to take screenshot.
I've included <script src="https://www.gstatic.com/firebasejs/5.5.5/firebase.js"></script> in index.html
I also installed firebase(latest version) via npm with --save
And created 2 files: firebaseInit.js and firebaseConfig.js(which contains the api key, etc for web app):
In firebaseInit.js:
import firebase from 'firebase';
import 'firebase/firestore';
import firebaseConfig from './firebaseConfig';
const firebaseApp=
firebase.initializeApp(firebaseConfig);
export default firebaseApp.firestore();
and put:
export default { the config key value pairs obtained
from Firebase console }
in firebaseConfig.js.
Extra info(though I don't think this is the source of the problem):
I imported firebase with import firebase as 'firebase' in the vue file I used firebase auth() and createUserWithEmailandPassword() in the methods property, but alert() doesn't work.
import firebase from 'firebase';
export default {
name: 'register',
data: function() {
return {
email: '',
password: ''
};
},
methods: {
register: function(e) {
firebase
.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(
user => {
alert(`Account made for ${user.email}`);
this.$router.push('/');
},
err => {
console.log(err.message);
}
);
e.preventDefault();
}
}
};
You created your firebaseInit.js but since you never import or require it, is it never executed, thus resulting in a "firebase not initialized" error when you try to use the firebase you imported from the generic firebase module instead.
Use export default firebaseApp in your firebaseInit.js (and not firestore as you do now) and then import firebase from 'path/to/firebaseInit.js' for example.

Implement callable cloud functions in Firebase client app

I have recently discovered the Firebase callable functions which allows me to call HTTPS trigger like function from the client side (and with auth() support).
I struggle to implement this new feature in my already existing Firebase web-client application.
I have some cloud functions running, among them are some HTTPS functions I would like to transform into an HTTPS callable function (with functions.https.onCall).
The documentation indicates:
Set up your client development environment
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-functions.js"></script>
And my code is:
import firebase from 'firebase';
import 'firebase/firestore';
const firebaseApp = firebase.initializeApp({
apiKey: '....',
authDomain: '....',
databaseURL: '....',
projectId: '....',
storageBucket: '....',
messagingSenderId: '....',
});
const db = firebaseApp.firestore();
const auth = firebaseApp.auth();
const functions = firebaseApp.functions();
export { db, auth, functions };
When I run my app, I got the following error:
Uncaught TypeError: firebaseApp.functions is not a function
I have tried yarn add firebase-functions and then import 'firebase-functions but then the app requires firebase-admin. I am affraid it is too much for a client-app so I might go in the wrong direction.
Can someone help me with this issue?
(!) This issue is NOT about the server-side Firebase SDK for Cloud Functions (Node JS). It is about calling Cloud functions directly from a Firebase web app.
Thank you!
UPDATE:
Thanks to #Andrew's post, this solves my issue:
My configuration
import firebase from 'firebase';
import 'firebase/firestore';
import '#firebase/functions';
import firestoreConfig from '#/config/firestore';
const firebaseApp = firebase.initializeApp(firestoreConfig /* The JSON configuration from my Firebase project */);
const db = firebaseApp.firestore();
const auth = firebaseApp.auth();
const functions = firebaseApp.functions();
export { db, auth, functions };
Using the configuration:
import { db, functions } from '#/database/firestoreInit';
export default {
addMessage(text) {
const addMessage = functions.httpsCallable('addMessage');
return addMessage({ text }).then(result => result);
},
}
I just ran into this same problem myself and solved it by installing and importing the #firebase/functions npm package. I found the solution on github here:
https://github.com/firebase/firebase-js-sdk/blob/master/packages/functions/README.md
From the README on github:
ES Modules
import firebase from '#firebase/app';
import '#firebase/functions'
// Do stuff w/ `firebase` and `firebase.functions`
CommonJS Modules
const firebase = require('#firebase/app').default;
require('#firebase/functions');
// Do stuff with `firebase` and `firebase.functions`
Hope that helps! The actual documentation is not very clear about having to do this in order to call the functions.
About #firebase/functions:
This package is not intended for direct usage, and should only be used via the officially supported firebase package.
This worked for me:
import * as firebase from 'firebase/app'; // Typescript
// import firebase from 'firebase/app'; // JS
import 'firebase/functions';
const myCallableFunc = firebase.functions().httpsCallable('myCallableFunc');
I don't know about importing firebase-functions with a CDN but if you're using npm then you don't need the firebase-functions package, just installing firebase will do.
Follow the steps mentioned here. Firebase cloud functions
I think there is nothing like firebaseApp.functions.

Resources