Nuxt.js plugin for Firebase - server side - javascript

I'm trying to connect Nuxt 2.14 with firebase firestore
I'm writing a plugin for firebase that runs on the server
I'm using "inject" to pass firebase.firestore() object as db.
But, when I'm using the $db I just injected
and try to make a data variable equal to $db via asyncData,
I get an error:
"Maximum call stack size exceeded"
I guess the firebase.firestore() object has a "circular structure"
Any suggestions?
Should I approach this differently?
Thank you
Here is the code for the different files I'm using:
.env
FB_DB_PATH=XXXXXX
FB_API_KEY=YYYYYY
FB_PROJECT_ID=ZZZZZZ
FB_AUTH_DOMAIN=EEEEEEE
nuxt.config.js
plugins: [
{ src: '~plugins/firebase.js', mode: 'server' },
// runs on server to get the firebase privateRuntimeConfig params
],
publicRuntimeConfig: {
baseURL: process.env.BASE_URL // just for test
},
privateRuntimeConfig: {
fbProjectID: process.env.FB_PROJECT_ID,
fbDBPath: process.env.FB_DB_PATH,
fbApiKey: process.env.FB_API_KEY,
fbAuthDomain: process.env.FB_AUTH_DOMAIN
},
firebase.js - this is the plugin file
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
export default function (context, inject) {
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: context.$config.fbApiKey,
authDomain: context.$config.fbAuthDomain,
//databaseURL: context.$config.fbDBPath,
projectId: context.$config.fbProjectID
})
}
let auth = firebase.auth();
let db = firebase.firestore();
inject('db', db);
}
in page code:
asyncData ( context ) {
return { db: context.$db }
}

AFAIK, what you're trying to do won't work, and is not secure anyways. Injecting your Firebase auth and firestore will inherently expose the API key client side, because they still need to use it there to communicate with the Firebase.
Further, passing back an object with circular references doesn't work, as Vue cannot serialize it properly, and wouldn't know how to deserialize it on the client either.
If you're just fetching data from Firebase at page load, then you should do so in the asyndData method, and return the fetched data. However, if you need to make calls during runtime, such as storing user data, then you will need an API to keep the keys secure. I usually recommend starting with Nuxts serverMiddleware, as it works out of the box on Nuxt projects.

Related

How to enable `ignoreUndefinedProperties` in node js

I am developing a REST api for my application in Nodejs and Express. But each time i try to send a request I get an undefined error. Please how can i enable 'ignoreundefinedproperties'
once you import the firebase-admin sdk (in Typescript) like this
import * as firestore from "firebase-admin";
then you just need to set the setting like this :
const db = firestore.firestore();
db.settings({ ignoreUndefinedProperties: true })
If you are getting errors for calling this more than once, I recommend putting admin.firestore().settings({ignoreUndefinedProperties:true}); in the same place you have admin.initializeApp();
Here's how I did it in my Typescript project
initialize.ts:
import * as admin from 'firebase-admin';
import 'firebase-functions';
admin.initializeApp();
admin.firestore().settings({ignoreUndefinedProperties:true});
Top of index.ts:
import './initialize.ts'
For anyone using the v9 API:
import { getFirestore, connectFirestoreEmulator, initializeFirestore } from 'firebase/firestore'; // Firebase v9+
// Must be called before getFirestore()
initializeFirestore(app, {
ignoreUndefinedProperties: true
});
const firestore = getFirestore(app);
If you're facing error (Firestore has already been initialized. You can only call settings() once) even after trying other answers, restart your IDE/VSCode and terminal. This is what worked for me.

Cannot read property 'firestore' of null when trying to initialize firebase in nuxt app

I am trying to use Firebase and Firestore in my nuxt application. I have done everything according to the documentation, but something seems to be wrong.
Obviously, if (!firebase.apps.length) evaluates to false, so app stays null, but I'm not initializing firebase from anywhere else, so this shouldn't happen
plugins/firebase.js
import firebase from "firebase";
import firestore from "firebase/firestore"; //linter says that firestore is declared but never read, which is weird, since it is.
const firebaseConfig = {
//config data of my project (not sure if I can share them, so I don't)
};
let app = null;
if (!firebase.apps.length) {
app = firebase.initializeApp(firebaseConfig);
}
export const db = app.firestore();
nuxt.config.js
plugins: ["~/plugins/firebase.js"],
layouts/default.vue
<script>
import * as firebase from "firebase/app";
</script>

Auth0 authentication with Gridsome server side rendering breaks with window is undefined

I've implemented the auth0 Vuejs according to their tutorial with Gridsome, and it worked fine in develop.
However, when I run gridsome build the build fails because window is undefined in a server context.
I've found a few issues in Auth0-js lib that claim that Auth0 should only be used in client side, however, due to the way Gridsome works, I can't seem to find a way to only load the Auth0-js in client side.
Gridsome has main.js where I would add plugins, and in there, I define the routing for authentication.
Main.js
import AuthServicePlugin from '~/plugins/auth0.plugin'
import auth from '~/auth/auth.service'
export default function (Vue, { router, head, isClient }) {
...
Vue.use(AuthServicePlugin)
//Handle Authentication
router.beforeEach((to, from, next) => {
if (to.path === "/auth/logout" || to.path === "/auth/callback" || auth.isAuthenticated()) {
return next();
}
// Specify the current path as the customState parameter, meaning it
// will be returned to the application after auth
auth.login({ target: to.path });
})
Based on a Gatsbyb.js auth0 implementation tutorial, I've tried to exlude auth0-js from webpack loading with null-loader
gridsome.config.js
configureWebpack: {
/*
* During the build step, `auth0-js` will break because it relies on
* browser-specific APIs. Fortunately, we don’t need it during the build.
* Using Webpack’s null loader, we’re able to effectively ignore `auth0-js`
* during the build. (See `src/utils/auth.js` to see how we prevent this
* from breaking the app.)
*/
module: {
rules: [
{
test: /auth0-js/,
use: 'null-loader',
},
],
},
I would love to get some ideas about how to include and load Auth0 only in client side context with Gridsome
I had the same problem with using Firebase Authentication with Gridsome.
It seems that code in the created() lifecycle hook gets executed in the Gridsome build process (which is a server environment), but code in the mounted() lifecycle hook only executes in the browser!
The solution was to put all the code that should only run in the client in the mounted lifecycle hook.
mounted() {
// load the `auth0-js` here
...
}
In my instance (with Firebase Auth) this was the solution:
In the Default Layout component:
const app = import("firebase/app");
const auth = import("firebase/auth");
const database = import("firebase/firestore");
const storage = import("firebase/storage");
Promise.all([app, auth, database, storage]).then(values => {
// now we can access the loaded libraries 😍!
});
}

Firestore arrayUnion

I'm building a basic CRUD app with vue.js and firebase. I'm trying to build out a favorites functionality and have ran into a persistent problem storing the data.
When a using clicks the add to favorites button I'm attempting to add the document id to an array in a "user profile" document. Here's the code:
export function addNewUserAction (type, key, userID){
console.log(userID);
console.log(key);
firebase.firestore().collection('userActions').add({
type: type,
listing: key,
user: userID,
time: Date.now()
})
if(type === 'favorite'){
var sendfav = db.collection('userProfiles').doc(userID).update({
favs: firebase.firestore.FieldValue.arrayUnion(key)
});
}else if(type === 'xout'){
var sendxout = db.collection('userProfiles').doc(userID).update({
xouts: firebase.firestore.FieldValue.arrayUnion(key)
});
}else{
console.error(type + " is not a user action");
}
}
I get the following error in the console:
Uncaught TypeError: Cannot read property 'arrayUnion' of undefined
at addNewUserAction
I have firebase and the db ref imported, and have ran a bogus .set() through each reference to confirm they are pointing at the right spots. I also already have the arrays created in 'userProfile' document.
The firebase install is fresh, like last week fresh, so I should have the function in my build.
Seems that arrayUnion is just not working. Any ideas? The other firestore functions are workin so I'm not sure. Am I blatenly missing something? Thanks
If you are using the Admin SDK
I was having some random errors similar to this, among others as I was experimenting. It turns out it was because I was using firebase admin sdk which requires a slight change compared to the web SDK documentation. If you are using firebase admin, replace
firebase.firestore.FieldValue.arrayUnion(...
with
admin.firestore.FieldValue.arrayUnion(...
I had the same issue...
This would not work
import { fireStore } from '../../firebase';
....
fireStore.collection("users").doc(props.uid).update({
points: fireStore.FieldValue.arrayUnion({value: pointObj.value, reason: pointObj.reason})
});
I changed the import and used the exact code from the Firebase Docs.
This works fine.
import * as firebase from 'firebase';
....
fireStore.collection("users").doc(props.uid).update({
points: firebase.firestore.FieldValue.arrayUnion({value: pointObj.value, reason: pointObj.reason})
});
Hope that helps.
Just wanted to update this. I was able to get this working by importing firebase the following way:
`import firebase from "firebase/firebase";`
I think my issue before was several problems, the first primarily was not having the correct version. I just recently updated to 5.8.4 which completely broke my app. I tried the above as a possible solution and it got my app working again. That led me to try it on with arrayUnion and it worked. Hopefully thats helpful to someone. Thanks all for the help.
Update 10/11/19
So I wanted to add another update to this in case someone new to firebase is chasing their tail with this as I have been.
So the solution I included above works because it uses the entire firebase SDK package ('firebase/firebase') which is fine, however you will importing the entire firebase package which is not ideal. You constantly see an alert in your console that you're using the development SDK
If you're trying to optimize your app and only import the firebase packages you need ( i.e. auth, datatbase, etc.) you need to import the firebase app object from 'firebase/app' then import the required packages. The key is to import as an object as the firebase docs call for:
import * as firebase from 'firebase/app';
Import 'firebase/auth'
If you use:
import firebase from 'firebase/app' this will not work.
Hope that helps someone. I know it's probie stuff but it stumped me for a while in my learning curve.
Firestore - Pass Array To arrayUnion()
let myArray = ["1", "2", "3"];
docRef.update({
test: firebase.firestore.FieldValue.arrayUnion.apply(this, myArray)
});
This worked for me^
For Vue.js Developers
I have created a folder named firebase in src folder, then i have created a file named init.js in firebase folder
init.js
import * as firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
// Your Firebase Config
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// utils
const db = firebase.firestore();
const auth = firebase.auth();
export { db, auth, firebase };
Now use them in whichever components you need.
Just by,
import { firebase, auth, db } from "#/firebase/init.js";
Now You can use firebase without any Errors
db.collection("users") // Collection Name
.doc(userId) // Document in Collection indexed userId
.update({
friends: firebase.firestore.FieldValue.arrayUnion(auth.currentUser.uid)
})
// friends is the array collection in firestore
Thats It

TypeError: firebase.storage is not a function

Following this example, I keep getting the error:
TypeError: firebase.storage is not a function
From this line in my code:
var storageRef = firebase.storage().ref();
(And when I simply try to initialize storage from the storage guide, linked from firebase's npm site, I get the same error.)
In my Node.js project, I'm including the following libraries:
const firebase = require('firebase');
var admin = require('firebase-admin');
const fs = require('fs');
Up to this point, I've successfully been able to read from and write to the firebase database, creating a reference to the database with var db = admin.database(), then var ref = db.ref("/")... So I know I've configured Firebase and firebase-database correctly. But I'm stuck on storage, and have tried both admin.storage().ref() and firebase.storage().ref(), and firebase.storage().ref("/") with the same error message.
I've also tried:
var storage = firbase.storage();
var storageRef = storage.ref();
and
const app = firebase.initializeApp(config);
var storage = app.storage();
and with ref()'s void argument () and with "/"... but have the same message, yet to no avail.
I'm using:
"firebase": "^3.6.4"
"firebase-admin": "^4.0.4"
Node.js : v6.9.1
What must I do to successfully create a reference to storage?
I faced the same problem. In my case, I needed to include storage module besides Firebase core.
import firebase from 'firebase/app';
import 'firebase/storage'; // <----
firebase.initializeApp({
...
});
const storageRef = firebase.storage().ref();
(npm firebase v5.0.4)
DEPRECATED, see below:
According to this answer, instead of firebase storage, in Node.js, google-cloud package storage should be used, and it seems that this answer should confirm it. Code example:
npm i --save google-cloud
Then:
const gcloud = require('google-cloud')
const storage = gcloud.storage({
projectId: '<projectID>',
keyFilename: 'service-account-credentials.json',
});
const bucket = storage.bucket('<projectID>.appspot.com')
As of 2018, this is the correct answer:
Or using only the storage part of the package:
npm install --save #google-cloud/storage
And then:
var storage = require('#google-cloud/storage')
Also check the docs for more.
Deprecated: please see the accepted answer.
Some details to note:
Firebase Storage is no longer used with Node.js, so all documentation there is useless for Node.js. Instead, use google-cloud. The references and guides for Firebase and Google Cloud do not reflect this as of today.
Unlike Firebase, google-cloud costs money, even for small projects.
In my case, I'm using the firebase-admin SDK so I don't have to mess with user authentication at the moment.
Purpose
To create a single Node.js project which uses Firebase and Google Cloud. Why? Firebase has a useful database, among other features, and Google Cloud allows cloud file storage and retrieval.
Directions
Step 1: Project Creation
Create Firebase and Google Cloud (Storage) projects.
Step 2: Install Packages
Using npm, install firebase-admin and google-cloud in Node.js project.
Note 1: I used the admin SDK, so after creating the Firebase project, you'll need to go to:
Settings(the gear) > Project Settings > Service Accounts > Firebase Admin SDK
Then you: Select Node.js > [Copy/paste the generated code into
your project] > [click "Generate New Private Key"] > [download the
generated json to preferred location] > [replace
"path/to...AccountKey.json" with the path to the key you just
generated]
Note 2: the generated key can be reused in firebase or google-cloud credentials.
Step 3: Firebase Setup
Once your project is created, import the firebase-admin sdk:
The code should look like this, but filled with your info:
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert("/path/to/generated/json/here.json"),
databaseURL: "database-url-from-firebase"
});
To find the databaseURL, go to 'Storage' in Firebase, and note the URL starting with gs: and copy/paste it the the value field of databaseURL.
Next, get a reference to the database you can use:
var db = admin.database();
var ref = db.ref("/");
console.log('DB ref: ' + ref); //just to debug, if undefined, there's a problem.
To learn more about reading/writing to the database, follow Firebase's own documentation.
Step 4: Google-Cloud Billing Setup
After creating a project on Google Cloud, add billing information; buckets cannot be used without billing info.
Step 5: Google-Cloud Storage Setup
Scrolling through the menu (the horizontal 3-bars), click "Storage", then "Enable Billing". Yes, you added billing info, now you need to enable it for that project's buckets.
You should see that a bucket should already exists from your Firebase project.
Click on menu again(3-bar icon), then > IAM & Admin > Settings
At settings, you'll see "Project ID" which should look like "projectName-00000" or "projectName-Some#", copy that project ID
Step 6: Google Cloud in Node.js
In your index.js:
var gcloud = require('google-cloud');
var gcs = gcloud.storage({
projectId: 'paste-that-project-id-here',
keyFilename: 'paste-that-path-to-the-previously-downloaded-json-from-firebase-here'
});
Now you can send a file to your storage by:
var bucket = gcs.bucket('bucket_name');
var remoteFile = bucket.file('somefile-inThisCaseASong.mp3');
var localFilename = '/Users/you/Music/somefile-inThisCaseASong.mp3';
bucket.upload(localFilename, function(err, file) {
if (!err) {
console.log('somefile-inThisCaseASong.mp3 is now in your bucket.');
} else {
console.log('Error uploading file: ' + err);
}
});
Step 7: Verify
If the file is visible in Firebase Storage and Google Cloud Storage, you're done!
Year 2020 answer, In my case I include firebase-storage.js in .html file
<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-storage.js"></script>
Therefore, if you use all Firebase service, you will have
<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-messaging.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-storage.js"></script>
<!-- your script calling Firebase Firestore under this line -->
<script>
....
</script>
I had the same problem, I had my code as following:
import * as firebase from "firebase/app";
import 'firebase/storage';
firebase.initializeApp({
...
});
const storageRef = firebase.storage().ref();
So, I found that way is only if you use Typescript.
If you use only ES6, then you must have:
import firebase from 'firebase/app';
import 'firebase/storage';
firebase.initializeApp({
...
});
const storageRef = firebase.storage().ref();
If you use ES5, then you must have:
var firebase = require("firebase/app");
require("firebase/storage");
firebase.initializeApp({
...
});
const storageRef = firebase.storage().ref();
Moreover, you can also use the following way but it is not recommended because you load all services (database,auth,storage,etc):
import firebase from "firebase";
firebase.initializeApp({
...
});
const storageRef = firebase.storage().ref();
Tested with Firebase 7.15.2
When using Storage with Firebase, you're correct that you can't add buckets on the free tier. However, you DO get a bucket (just one) by default. My (eventually) successful approach was to:
Add Storage to my project in Firebase (NOT Google Cloud)
Add the Admin SDK and set up the necessary Service Account as per the Google Docs: https://firebase.google.com/docs/admin/setup?authuser=1
Add the #google-cloud/storage package as per the instructions on using the Admin SDK with storage: https://firebase.google.com/docs/storage/admin/start?authuser=1
Initialize the app:
admin.initializeApp({
credential: admin.credential.cert("/path/to/generated/json/here.json"),
storageBucket: "folder-URL-from-Storage-page-excluding-gs://"
});
Access the bucket object with (from Admin SDK docs):
const bucket = admin.storage().bucket();
Operate on the bucket with the storage library. Example:
bucket.upload('/path/file.ext', function(err, file, apiResponse) {
//Do Stuff
});
NOTE: I spent a couple of hours convinced it wasn't working because I didn't have a bucket, but it turned out my mistake was including the gs:// in the path to my storage bucket when initializing.
UPDATED FOR VERSION 9
import { initializeApp } from "firebase/app";
import { getStorage } from "firebase/storage";
// Set the configuration for your app
// TODO: Replace with your app's config object
const firebaseConfig = {
apiKey: '<your-api-key>',
authDomain: '<your-auth-domain>',
databaseURL: '<your-database-url>',
storageBucket: '<your-storage-bucket-url>'
};
const firebaseApp = initializeApp(firebaseConfig);
// Get a reference to the storage service, which is used to create references in your storage bucket
const storage = getStorage(firebaseApp);
Source: https://firebase.google.com/docs/storage/web/start
make sure you have added a script tag with this src in you HTML file < src="https://www.gstatic.com/firebasejs/7.12.0/firebase-storage.js" to your project
Here 7.12.0 will be replaced with your current working version.
I think V9 of firebase has a different way of letting us use its storage. For using firebase storage you can import storage as -
import { getStorage } from "firebase/storage";
Now you can follow this -
const firebaseApp = firebase.initializeApp(firebaseConfig);
const storage = getStorage(firebaseApp);
Does it change if you try to do it like the following?
// Initialize the default app:
const app = firebase.initializeApp(appConfig)
// Initialize storage from the *app* object:
const storage = app.storage()
From the docs, it seems that this is an alternate method. I prefer this one, as this links the storage to the app, making the code more readable, and more functional
I was able to use firebase.storage(), but it took some time to figure it out. It only works when importing like this:
//Importing
const firebase = require('firebase')
require('firebase/storage')
//Calling the function (You can call it normally then)
const storageRef = firebase.storage().ref();
I'm serious, whenever trying to import as firebase.storage() or doing anything different it wouldn't work! Hope it helps some of you guys.
in app.module initilize
import firebase from 'firebase/app';
import 'firebase/storage';
firebase.storage();
It looks like they have fixed it in a new version of the library.
Just run npm update on the relevant package(s) and it should work.
I encounter the same problem when I test my app locally, but everything works when the project is deployed.
This way, I actually use the following workaround :
if (!firebase.storage) {
// prevent crash when working locally
return;
}
let ref = firebase.storage().ref()
// perform production stuff ...
It's a little bit curious, but it works in my case.
The firebase storage service still works (and AFAIK is not deprecated) in browsers. The firebase storage service is not available on server environments including during server-side-rendering.
This is how I've done it:
// only import uploadHandler client-side.
const uploadHandler = typeof window === 'object' ? require('./handlers').imageHandler : () => false; // ssr only
Then in handlers.js you can safely use the firebase storage service normally.
import * as firebase from 'firebase'
const storageRef = firebase.storage().ref();
const bucketObjectRef = storageRef.child('myFile.jpg');
As of 4/3/2020, it seems the entire firebase suite of products is still supported in browser environments.
Source: https://firebase.google.com/docs/web/setup#namespace
I encountered a similar problem when I was integrating firebase's storage on the browser (webapp)
<script src="https://www.gstatic.com/firebasejs/7.15.0/firebase-storage.js"></script>
Just add that line, and the bug is gone!
import * as firebase from 'firebase/app';
Then firebase.storage().ref() should work.
Hope this helps.

Categories