Read Collection in firebase from vue js - javascript

I am trying to read a collection with specific name from firebase firestore, but I get an error which I couldn't find answer for it.
Here you can see my boot file:
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth, createUserWithEmailAndPassword } from "firebase/auth";
import { collection, query, where, doc, getDoc } from "firebase/firestore";
const firebaseApp = initializeApp({
apiKey: "####",
authDomain: "####",
projectId: "####",
});
const db = getFirestore();
const auth = getAuth();
const createUser = createUserWithEmailAndPassword();
export default {db, auth, createUser};
Here is my script tag in .vue file:
<script>
import { collection, getDocs } from "firebase/firestore";
import db from "src/boot/firebase"
export default {
setup() {
return {
waitingList: {},
};
},
created() {
const querySnapshot = await getDocs(collection(db, "waitingList"));
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.name, " => ", doc.data());
});
},
};
</script>
and here you can see the list of errors I got:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'tenantId')
FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore

The imported db would be an object containing instances of Firestore and Auth. Try changing your import as shown below:
import { db } from "src/boot/firebase"
// instead of import db from "..."
export default {
setup() {
return {
waitingList: {},
};
},
async created() {
const querySnapshot = await getDocs(collection(db, "waitingList"));
querySnapshot.forEach((doc) => {
console.log(doc.name, " => ", doc.data());
});
},
};
Also change export default { db } to export { db } in the boot file.

Related

Firestore get count for a collection not working

I have this code:
import { getCountFromServer } from 'firebase/firestore'
useEffect(()=> {
const getCount = async () => {
const collection = userDB().collection('invoices')
const query = collection.where('status', '==', 'paid')
const snapshot = await getCountFromServer(query)
setCount(snapshot.data().count)
}
getCount().catch(err=> console.log(err))
}, [user, collectionRef, filterKey, filterValue])
Where userDB() is the same as db.collection('users').doc(userid')
Yet this throughs an error:
TypeError: Cannot read properties of undefined (reading '_t')
at gn (index.esm2017.js:4251:1)
at bu (index.esm2017.js:13685:1)
at Ca.run (index.esm2017.js:16797:1)
at index.esm2017.js:20050:1
I have followed the docs: https://firebase.google.com/docs/firestore/query-data/aggregation-queries#web-version-9_1
Currently using:
"firebase": "^9.15.0",
userDB is defined in a ts file like so:
export const userDB = () => {
let user = auth.currentUser
return db.collection('users').doc(user?.uid)
}
Here is the initlizer file:
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';
const firebaseApp = firebase.initializeApp ({
...keys
});
export const Providers = {
google: new firebase.auth.GoogleAuthProvider(),
};
const db = firebaseApp.firestore()
const auth = firebase.auth();
const Fire = firebaseApp
export {db, Fire, auth}
You should not uses both compat and modular SDKs together. The compat version does not support count queries. I would recommend updating your code to use the new syntax:
import { initializeApp } from 'firebase/app';
import { getFirestore } 'firebase/firestore';
import { getAuth, GoogleAuthProvider } 'firebase/auth';
const firebaseApp = initializeApp({
...keys
});
export const Providers = {
google: new GoogleAuthProvider(),
};
const db = getFirestore()
const auth = getAuth();
export { db, auth }
import { db } from '...'
import { collection, query, where, getCountFromServer } from 'firebase/firestore'
useEffect(()=> {
const getCount = async () => {
const q = query(collection(db, 'invoices'), where('status', '==', 'paid'))
const snapshot = await getCountFromServer(q)
setCount(snapshot.data().count)
}
getCount().catch(err=> console.log(err))
}, [user, collectionRef, filterKey, filterValue])

Why firebase give this error when I use onSnapshot?

Error itself : Uncaught FirebaseError: Expected type 'pa', but it was: a custom $n object
firebase file :
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore/lite'
const firebaseConfig = {
apiKey: 'API_KEY',
authDomain: 'AUTH_DOMAIN',
projectId: 'PROJECT_ID',
storageBucket: 'STORAGE_BUCKET',
messagingSenderId: 'MESSAGING_SENDER_ID',
appId: 'APP_ID',
}
const firebaseApp = initializeApp(firebaseConfig)
const db = getFirestore(firebaseApp)
const auth = getAuth(firebaseApp)
export { db, auth }
Request itself :
useEffect(() => {
//getPosts()
const unsubscribe = onSnapshot(collection(db, 'cities'), (snapshot) => {
const postsList = snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
setPosts(postsList)
})
return () => {
unsubscribe()
}
}, [])
I tried to change some imports like other recommend but just got another error
Firestore Lite SDK does not support listeners. Try importing getFirestore() from the standard SDK.
import { initializeApp } from 'firebase/app'
import { getAuth } from 'firebase/auth'
import { getFirestore } from 'firebase/firestore' // <-- remove /lite
const firebaseConfig = {...}
const firebaseApp = initializeApp(firebaseConfig)
const db = getFirestore(firebaseApp)
const auth = getAuth(firebaseApp)
export { db, auth }
import { db } from './path/to/firebase';
import { collection, onSnapshot } from 'firebase/firestore';
useEffect(() => {
const unsubscribe = onSnapshot(collection(db, 'cities'), (snapshot) => {
const postsList = snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
}))
setPosts(postsList)
})
}, [])

NextJs getServerSideProps cannot fetch data from Cloud Firestore Web version 9

I am using NextJs version 12.0.10 and firebase version 9.6.6 which use a modular system to import it.
When I run the function from my service to fetch data from firebase/firestore, it returns an error saying Cannot access 'getStories' before initialization. I'm confident all the logic & syntax are correct, as it works perfectly when I fetch it from inside the page render function.
Here is my getServerSideProps function:
pages/index.js
import '#uiw/react-markdown-preview/markdown.css';
import { useContext } from 'react';
import { getStories } from '../lib/services/StoryService';
import { truncate } from 'lodash';
import { convertSecondsToHumanReadableDiff } from '../lib/utils/common';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { AuthContext } from '../pages/_app';
import Navigation from '../components/Navigation';
export async function getServerSideProps() {
const fetchedStories = await getStories();
const stories = fetchedStories.docs.map((story) => {
return {
...story.data(),
id: story.id,
content: truncate(story.data().content, { length: 150, separator: '...' }),
};
});
return { props: { stories } };
}
const Blog = ({ stories }) => {
const router = useRouter();
const { user } = useContext(AuthContext);
return (
<div>
...
</div>
);
};
export default Blog;
lib/firebase/firebase.js
import { initializeApp } from 'firebase/app';
import { getAnalytics } from 'firebase/analytics';
import { getFirestore } from 'firebase/firestore';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey: 'XXX',
authDomain: 'XXX',
projectId: 'XXX',
storageBucket: 'X',
messagingSenderId: 'XXX',
appId: 'XXX',
measurementId: 'XXX',
};
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
export const database = getFirestore(app);
export const auth = getAuth(app);
lib/services/storyService.js
import {
collection,
query,
getDocs,
getDoc,
setDoc,
doc,
serverTimestamp,
orderBy,
} from 'firebase/firestore';
import { database } from '../firebase/firebase';
import slugify from 'slugify';
import { random } from 'lodash';
const storiesRef = collection(database, 'stories');
export const createStory = async (payload) => {
const slugTitle = slugify(payload.title);
const slug = slugTitle + '-' + random(0, 100000);
const updatedPayload = {
...payload,
slug,
type: 'published',
createdAt: serverTimestamp(),
};
return setDoc(doc(storiesRef, slug), updatedPayload);
};
export const getStories = async () => {
const q = query(storiesRef, orderBy('createdAt', 'desc'));
return getDocs(q);
};
export const getStoryBySlug = async (slug) => {
const docRef = doc(database, 'stories', slug);
return getDoc(docRef);
};
You are using getDocs, a client-side function of firebase, inside your getStories function, which is invoked in getServerSideProps, a node.js environment.
Instead you need to use the admin SDK for functions invoked in node.js environment (like getServerSideProps), e.g. like so:
import * as admin from "firebase-admin/firestore";
export const getStories = async () => {
return await admin
.getFirestore()
.collection(database, 'stories')
.orderBy('createdAt', 'desc')
.get()
};
export const getStoryBySlug = async (slug) => {
return await admin
.getFirestore()
.doc(database, 'stories', slug)
.get()
};
(sorry for the late answer, I still hope it helps OP or anyone else)

_firebase_config__WEBPACK_IMPORTED_MODULE_3__.default.createUserWithEmailAndPassword is not a function in Vue Js

createUserWithEmailAndPassword function is not working for me. Below are my code -
config.js
import firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/auth'
const firebaseConfig = {
apiKey: "AIzaSyBD8W5T7ZSvryW2TNSWOgoCO3EpyV6i65o",
authDomain: "vue-firebase-site-eb79e.firebaseapp.com",
projectId: "vue-firebase-site-eb79e",
storageBucket: "vue-firebase-site-eb79e.appspot.com",
messagingSenderId: "657936011344",
appId: "1:657936011344:web:a2498d2fe27f951b6b8155"
};
firebase.initializeApp(firebaseConfig)
const projectAuth = firebase.auth();
const projectFirestore = firebase.firestore();
const timeStamp = firebase.firestore.FieldValue.serverTimestamp
export default { projectAuth, projectFirestore, timeStamp }
useSignUp.js
import { ref } from "vue"
import projectAuth from '../firebase/config'
const error = ref(null)
const signup = async (email,password,displayName) => {
error.value = null
try {
const res = await projectAuth.createUserWithEmailAndPassword(email, password)
console.log(res)
if(!res){
throw new Error('Could not complete the signup')
}
await res.user.updateProfile({displayName})
error.value = null
return res
} catch (err) {
console.log(err.message)
error.value = err.message
}
}
const useSignup = () =>{
return{error, signup}
}
export default useSignup
Tried a number of things-
Delete node modules and install them again.
Change the version of firebase as well
Nothing working for me any solutions are appreciated.
Thanks in Advance!!
when you importing the config.js file inside useSignUp.js you are setting the whole object as projectAuth
what you need to do is following:
import { projectAuth } from '../firebase/config' // just get projectAuth variable from config.js
edit:
There is another way to work this around:
import firebaseConfig from '../firebase/config'
then inside the try block use this:
const res = await firebaseConfig.projectAuth.createUserWithEmailAndPassword(email, password)

How to use firebase auth and Cloud Firestore from different components as a single firebase App

I am trying to use firebase in my React project to provide the auth and database functionalities.
In my App.js I have
import app from "firebase/app";
import "firebase/auth";
app.initializeApp(firebaseConfig);
In my other components called <Component /> rendered by App.js I have this to initialize the database
import app from "firebase/app";
import "firebase/firestore";
const db = app.firestore();
However this time I got this error
Uncaught FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app).
So I tried to put app.initializeApp(firebaseConfig); in this component too but I got a new error again to tell me I instantiated twice.
Uncaught FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app).
So one workaround I came up with is to create a context at App.js and right after app.initializeApp(firebaseConfig); I created the database by const db = app.firestore(); and pass the value to the context and let the <Component /> to consume. However I don't know if this is a good solution or not.
My question is different from How to check if a Firebase App is already initialized on Android for one reason. I am not trying to connect to a second Firebase App as it was for that question. There is only one Firebase App for my entire project, to provide two services: auth and database.
I tried the solution from that question to use in <Component />
if (!app.apps.length) {
app.initializeApp(firebaseConfig);
}
const db = app.firestore();
but it didn't work it still gives me Uncaught FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app). error
You use different instances of Firebase in App and Component.
// firebaseApp.js
import firebase from 'firebase'
const config = {
apiKey: "...",
authDomain: "...",
databaseURL: "....",
projectId: "...",
messagingSenderId: "..."
};
firebase.initializeApp(config);
export default firebase;
Than you can import firebase from firebaseApp.js and use it. More details here
Make a file firebaseConfig.js in src/firebase directory for firebase configuration:
import firebase from 'firebase/app'; // doing import firebase from 'firebase' or import * as firebase from firebase is not good practice.
import 'firebase/auth';
import 'firebase/firestore';
// Initialize Firebase
let config = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
};
firebase.initializeApp(config);
const auth = firebase.auth();
const db = firebase.firestore();
const googleAuthProvider = new firebase.auth.GoogleAuthProvider();
const emailAuthProvider = new firebase.auth.EmailAuthProvider();
export { auth, firebase, db, googleAuthProvider, emailAuthProvider };
All you have to do in Component.js is:
import { db } from './firebase/firebaseConfig.js'; // Assuming Component.js is in the src folder
Store the api keys in a .env file in the root folder of the project (the parent of src):
REACT_APP_FIREBASE_API_KEY=<api-key>
REACT_APP_FIREBASE_AUTH_DOMAIN=<auth-domain>
REACT_APP_FIREBASE_DATABASE_URL=<db-url>
REACT_APP_FIREBASE_PROJECT_ID=<proj-name>
REACT_APP_FIREBASE_STORAGE_BUCKET=<storage-bucket>
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=<message-sender-id>
The error message you are receiving is valid and has to do with the order your modules are imported. ES6 modules are pre-parsed in order to resolve further imports before code is executed.
Assuming the very top of your App.js looks something like this:
import Component from '../component';
...
import app from "firebase/app";
import "firebase/auth";
app.initializeApp(firebaseConfig);
The problem here is that inside import Component from '.../component';
import app from "firebase/app";
import "firebase/firestore";
const db = app.firestore();
That code gets executed before you do:
app.initializeApp(firebaseConfig);
There's many ways to fix this problem including some solutions presented above and the proposal to just store your firebase config in a firebase-config.js and import db
from that.
This answer is more about understanding what the problem was ... and as far as the solution I think your Context Provider is actually really good and commonly practiced.
More about es6 modules here
Firebase React Setup
Hope that helps.
You can use a context as you said or redux (using a middleware to initialize, and global state to keep the db):
// Main (for example index.js)
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>
Firebase.js:
import app from 'firebase/app'
import 'firebase/firestore'
const config = {
apiKey: process.env.API_KEY,
databaseURL: process.env.DATABASE_URL,
projectId: process.env.PROJECT_ID,
storageBucket: process.env.STORAGE_BUCKET
}
export default class Firebase {
constructor() {
app.initializeApp(config)
// Firebase APIs
this._db = app.firestore()
}
// DB data API
data = () => this._db.collection('yourdata')
...
}
FirebaseContext.js:
import React from 'react'
const FirebaseContext = React.createContext(null)
export const withFirebase = Component => props => (
<FirebaseContext.Consumer>
{firebase => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
)
Then you can use withFirebase in your container components:
class YourContainerComponent extends React.PureComponent {
state = {
data: null,
loading: false
}
componentDidMount() {
this._onListenForMessages()
}
_onListenForMessages = () => {
this.setState({ loading: true }, () => {
this.unsubscribe = this.props.firebase
.data()
.limit(10)
.onSnapshot(snapshot => {
if (snapshot.size) {
let data = []
snapshot.forEach(doc =>
data.push({ ...doc.data(), uid: doc.id })
)
this.setState({
data,
loading: false
})
} else {
this.setState({ data: null, loading: false })
}
})
})
})
}
componentWillUnmount() {
if (this._unsubscribe) {
this._unsubscribe()
}
}
}
export default withFirebase(YourContainerComponent)
You can see the whole code here: https://github.com/the-road-to-react-with-firebase/react-firestore-authentication and a tutorial here: https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial/
If you implement it using redux, and redux-thunk you can isolate all firebase stuff in middleware, actions, and reducers (you can take ideas and sample here: https://github.com/Canner/redux-firebase-middleware); and keep the business logic in your components so they do not need to know how your data collections are stored and managed. The components should know only about states and actions.
The best way I have found to use firebase in react is to first initialize and export firebase to then execute the desired functions.
helper-firebase.js
import * as firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
// Everyone can read client side javascript, no need to use an .env file
// I only used environment variables for firebase-admin
import { FIREBASE_CONFIG } from '../../config';
// Initialize Firebase
firebase.initializeApp(FIREBASE_CONFIG);
export const auth = firebase.auth();
export const provider = new firebase.auth.GoogleAuthProvider();
export const db = firebase.firestore();
export default firebase;
your-component.js
import {
auth,
provider,
db,
} from '../../../helpers/helper-firebase';
...
componentDidMount() {
this.usersRef = db.collection('users');
// Look for user changes
auth.onAuthStateChanged(this.authChanged);
}
authChanged(user) {
// Set value on the database
this.usersRef.doc(user.uid).set({
lastLogin: new Date(),
}, { merge: true })
.then(() => {
console.log('User Updated');
})
.catch((error) => {
console.error(error.message);
});
}
login() {
auth.signInWithPopup(provider)
.then((res) => {
console.log(newUser);
})
.catch((error) => {
console.error(error.message);
})
}
...
But i would recommend use 'redux-thunk' to store data on state:
redux-actions.js
import {
auth,
} from '../../../helpers/helper-firebase';
export const setUser = payload => ({
type: AUTH_CHANGED,
payload,
});
export const onAuthChange = () => (
dispatch => auth.onAuthStateChanged((user) => {
// console.log(user);
if (user) {
dispatch(setUser(user));
} else {
dispatch(setUser());
}
})
);
export const authLogout = () => (
dispatch => (
auth.signOut()
.then(() => {
dispatch(setUser());
})
.catch((error) => {
console.error(error.message);
})
)
);
Here is a simple example of storing the signed-in user data from google OAuth into firestore collection.
Store firebase config in a separate file
firebase.utils.js
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
//replace your config here
const config = {
apiKey: '*****',
authDomain: '******',
databaseURL: '******',
projectId: '******,
storageBucket: '********',
messagingSenderId: '*******',
appId: '**********'
};
firebase.initializeApp(config);
export const createUserProfileDocument = async (userAuth) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt
});
} catch (error) {
console.log('error creating user', error.message);
}
}
return userRef;
};
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(provider);
App.js
import React from 'react';
import { auth, createUserProfileDocument, signInWithGoogle } from './firebase.utils';
class App extends React.Component {
constructor() {
super();
this.state = {
currentUser: null
};
}
unsubscribeFromAuth = null;
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
this.setState({
currentUser: {
id: snapShot.id,
...snapShot.data()
}
});
console.log(this.state);
});
}
this.setState({ currentUser: userAuth });
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return(
<React.Fragment>
{ this.state.currentUser ?
(<Button onClick={() => auth.signOut()}>Sign Out</Button>)
:
(<Button onClick={signInWithGoogle} > Sign in with Google </Button>)
}
</React.Fragment>
)
}
}
export default App;
I suggest you use store management libraries like Redux when you want to share the state between components. In this example, we have finished everything in a single component. But in realtime, you may have a complex component architecture in such use case using store management libraries may come in handy.

Categories