NextJS: Accessing Google Firestore data - firebase

So I'm not sure how to do this exactly. Here is what I have:
In my Index.js I have
Index.getInitialProps = async function () {
const firebase = require('firebase')
const firebaseConfig = {
apiKey: "examplezbxW_9nKoUjas",
authDomain: "example-prod.firebaseapp.com",
databaseURL: "https://example-prod.firebaseio.com",
projectId: "example-prod",
storageBucket: "example-prod.appspot.com",
messagingSenderId: "1234567890",
appId: "1:1234567890:web:1234567890"
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig)
}
const db = firebase.firestore()
const data = db.collection('data').get()
return {
data
}
}
This gives me an error of FirebaseError: projectId must be a string in FirebaseApp.options
(Maybe I need to run the firebase node package and login...)
It says the error is occurring at this line const db = firebase.firestore()
Any help is appreciated. Maybe this isn't the spot I should be trying to load firestore data... not sure.
I also considered creating a node server and doing it that way, but ideally, I'd like to avoid that.

Okay, so there was a couple changes I made to fix this.
I moved my firebase initialization to another file and that file looked like this:
import firebase from 'firebase/app'
import 'firebase/firestore'
export function loadDB() {
try {
var config = {
apiKey: "YOUR INFO HERE",
authDomain: "YOUR INFO HERE.firebaseapp.com",
databaseURL: "https://YOUR INFO HERE.firebaseio.com",
projectId: "YOUR INFO HERE",
storageBucket: "YOUR INFO HERE.appspot.com",
messagingSenderId: "YOUR INFO HERE",
appId: "YOUR INFO HERE"
};
firebase.initializeApp(config);
} catch (err) {
// we skip the "already exists" message which is
// not an actual error when we're hot-reloading
if (!/already exists/.test(err.message)) {
console.error('Firebase initialization error', err.stack);
}
}
return firebase;
}
Then, in my NextJS componenet, in the getInitialProps method, I have:
import { loadDB } from '../lib/db'
Index.getInitialProps = async function () {
const db = await loadDB()
let data = []
const querySnapshot = await db.firestore().collection('data').get()
querySnapshot.forEach(doc => {
data.push(doc.data())
})
return {
data
}
}

Related

Firebase Storage`uploadBytes`: "TypeError: Cannot read properties of undefined (reading 'byteLength')"

I'm trying to write a Firebase Cloud Function that uploads a file to Firebase Cloud Storage using uploadBytes. I'm following the documentation for web apps. Whatever I do throws this error:
TypeError: Cannot read properties of undefined (reading 'byteLength')
This error message isn't listed on the documentation page for error handling but I've deduced that the error message means that it can't find the file to upload. I'm getting this error with the emulator and with the cloud.
Let's start with uploadString, which works.
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getStorage, ref, uploadBytes, uploadString, connectStorageEmulator } from "firebase/storage";
const firebaseConfig = {
apiKey: "12345",
authDomain: "my-awesome-app.firebaseapp.com",
databaseURL: "https://my-awesome-app.firebaseio.com",
projectId: "my-awesome-app",
storageBucket: "my-awesome-app.appspot.com",
messagingSenderId: "12345",
appId: "12345"
};
initializeApp(firebaseConfig);
export const StringMe = functions.firestore.document('StringMe/{userID}').onUpdate((change, context) => {
const storage = getStorage();
// const storageRef = ref(storage, 'message.txt'); // location to write to
// connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud
const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/message.txt');
const message = "Hello world!";
async function uploadMessage() {
try {
await uploadString(storageRef, message);
console.log("Uploaded a string!");
} catch (error) {
console.error(error);
}
};
return uploadMessage()
});
This uploads a string to my Cloud Firestore and logs Uploaded a string! and then Finished "StringMe" in 417.150521ms. 60 seconds later it throws an error:
functions: Your function timed out after ~60s. To configure this timeout, see
https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.
⚠ Your function was killed because it raised an unhandled error.
That error seems to be a bug in the Firebase CLI, I ignore it.
Let's try this with the emulator. We'll comment out the storageRef and uncomment the two commented lines.
const storageRef = ref(storage, 'message.txt');
connectStorageEmulator(storage, "localhost", 9199);
That doesn't throw any errors (except the 60 second timeout), doesn't log anything, and nothing is written to Storage. Why doesn't the Storage Emulator work?
Now let's make a file to upload.
// my-module.js
export const file = "Hello world";
Then we'll upload it to Cloud Storage.
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getStorage, ref, uploadBytes, uploadString, connectStorageEmulator } from "firebase/storage";
import { file } from "./my-module.js";
const firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "..."
};
initializeApp(firebaseConfig);
export const ByteMe = functions.firestore.document('ByteMe/{userID}').onUpdate((change, context) => {
const storage = getStorage(app);
// const storageRef = ref(storage, 'hello.txt'); // location to write to
// connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud
const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/hello.txt');
const metadata = {
contentType: 'text/plain',
};
async function uploadFile() {
try {
console.log(file);
await uploadBytes(storageRef, file, metadata);
console.log('Uploaded a file!');
} catch (error) {
console.error(error);
}
}
return uploadFile();
});
This logs Hello world (we know that the file is available and readable within the function) and then throws this error:
TypeError: Cannot read properties of undefined (reading 'byteLength')
Something is undefined. Nothing has changed in the code except that the string became file. The error message must be saying that it can't read the file. byteLength seems to be a red herring, best ignored unless you like rabbit holes. Why can't uploadBytes read the file?
Switching to the emulator throws the same error message.
Let's try getting a file from an API and then uploading it to Storage.
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getStorage, ref, uploadBytes, uploadString, connectStorageEmulator } from "firebase/storage";
import got from 'got';
const firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "..."
};
initializeApp(firebaseConfig);
export const ByteAPI = functions.firestore.document('ByteAPI/{userID}').onUpdate((change, context) => {
const storage = getStorage();
// const storageRef = ref(storage, 'picture.jpg'); // location to write to
// connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud
const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/winter.mp3');
const metadata = {
contentType: 'audio/mpeg',
};
async function uploadFile() {
try {
let file = await got('https://audio.oxforddictionaries.com/en/mp3/winter__us_2.mp3');
await uploadBytes(storageRef, file, metadata);
console.log('Uploaded a file!');
} catch (error) {
console.error(error);
}
}
return uploadFile();
});
You can click on https://audio.oxforddictionaries.com/en/mp3/winter__us_2.mp3 and listen to the audio file.
This throws the same error, with Cloud Storage or the emulator:
TypeError: Cannot read properties of undefined (reading 'byteLength')
I also tried uploading a Uint8Array, same error message. Is uploadBytes broken?
My answer below works but is not best practices. I'm working on a best practices tutorial: https://github.com/tdkehoe/Cloud-Functions-for-Firebase-Tutorial. The short answer is, use uploadBytes from the front end, not from Cloud Functions. Use Node in Cloud Functions
--
I was able to download the audiofile from the Oxford English Dictionary API and upload it to Cloud Storage by changing file to file['rawBody']:
await uploadBytes(storageRef, file['rawBody'], metadata);
This didn't work for uploading the Hello world text file. The documentation says that uploadBytes will handle "JavaScript File and Blob APIs". This has to do with JSON file and buffers, which I don't understand. I'll keep working on this.

Why is Firebase Cloud Messaging not showing on foreground in my vue app?

package.json
"firebase": "^9.14.0",
public/firebase-messaging-sw.js
`
importScripts('https://www.gstatic.com/firebasejs/8.2.7/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/8.2.7/firebase-messaging.js')
try {
const config = {
apiKey: "AIzaSyBzFxb1h0INNwZ2dKMF83PfoMXa2aCdXqM",
authDomain: "jobdoh-dc1db.firebaseapp.com",
projectId: "jobdoh-dc1db",
storageBucket: "jobdoh-dc1db.appspot.com",
messagingSenderId: "922163524694",
appId: "1:922163524694:web:a50be771626fa1356077f7",
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(payload => {
console.log("Payload :> ", payload)
return self.registration.showNotification("hello", { body: "aaa" })
})
} catch (err) {
console.log("err in sw :> ", err)
}
All console.log that in the firebase-messaging-sw.js are not working.
src/plugins/firebase.js
import { initializeApp, getApps } from 'firebase/app';
import { getMessaging } from 'firebase/messaging'
import '#firebase/app';
const config = {
apiKey: "AIzaSyBzFxb1h0INNwZ2dKMF83PfoMXa2aCdXqM",
authDomain: "jobdoh-dc1db.firebaseapp.com",
databaseURL: "https://jobdoh-dc1db-default-rtdb.asia-southeast1.firebasedatabase.app",
projectId: "jobdoh-dc1db",
storageBucket: "jobdoh-dc1db.appspot.com",
messagingSenderId: "922163524694",
appId: "1:922163524694:web:a50be771626fa1356077f7",
measurementId: "G-VJ4952421E"
};
const apps = getApps();
const app = !apps.length ? initializeApp(config) : apps[0];
const messaging = getMessaging(app)
export default messaging;
src/index.js
import {createApp} from "vue";
import firebaseMessaging from './plugins/firebase';
const app = createApp();
app.config.globalProperties.$messaging = firebaseMessaging;
app.mount("#app");
I declare the messaging service as a global properties.
And use it in the customPage(eg. NotiPage.vue)
src/pages/NotiPage.vue
<template>
<v-btn #click="getToken">Get Token</v-btn>
</template>
<script>
import { getMessaging, onMessage, getToken } from "firebase/messaging";
export default {
methods: {
async getToken() {
const token = await getToken(this.$messaging, {
ServiceWorkerRegistration: "/public/firebase-messaging-sw.js",
vapidKey:
"BKOVTA3E_8xNS8MqBNVWxv8bxlaOman4gaY5jXpMG-wpev19uWFANeyKy6rRDxfx8QEarlUSqwJ7UNiuripRA4c",
});
if (token) {
console.log(token); // I got the token
} else {
// Request permission
}
},
},
mounted(){
const message = getMessaging();
onMessage(message, (payload) => console.log("Messages :> "), payload)
}
}
</script>
I got the token and message on the console. But not show the noti popup in my chrome. I turn on my notification permission on my computer setting and allow the notification on my chrome.
This is console.log image.
Pls advice me. Where did I wrong?
I want to appear notification popup on my browser and running the process in background.

Firestore works once and then it keeps throwing this error

I am trying to use this to get a snapshot of the firestore in a Nextjs project. But for some reason it works the first time but as soon as I refresh the page I get the following error and then I have to restart the server.
[FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore] {
code: 'invalid-argument',
customData: undefined,
toString: [Function (anonymous)]
}
import db from "../../firebase";
import { collection, getDocs, orderBy, query } from "firebase/firestore";
export async function getServerSideProps(context) {
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const session = await getSession(context);
//query from firestore
const colRef = collection(db, `users/${session.user.email}/orders`);
const q = query(colRef, orderBy("timestamp", "desc"));
const snapshot = await getDocs(q);
The db instance is imported from the firebase config file here.
import { getApp, initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: "some-apikey",
authDomain: "some-authDomain",
projectId: "some-projectId",
storageBucket: "some-storageBucket",
messagingSenderId: "some-messagingSenderId",
appId: "some-appId",
measurementId: "some-measurementId",
};
function createFirebaseApp(config) {
try {
return getApp();
} catch {
return initializeApp(config);
}
}
const app = createFirebaseApp(firebaseConfig);
const db = getFirestore(app);
export default db;
The query returns the correct data the first time but the second time the error appears. I have also tired with firebase/firestore/lite but the same error appears

firebaseConfig.default.auth promise rejection error

I am trying to signup user with facebook but i am seeing very rigid error and it seems there is no solved help out there so i am asking again
thats my code
async function signInWithFacebook() {
// const appId = Expo.Constants.manifest.extra.facebook.appId;
Facebook.initializeAsync(appid)
const permissions = ['public_profile']; // Permissions required, consult Facebook docs
const {
type,
token,
} = await Facebook.logInWithReadPermissionsAsync(
{permissions}
);
if(type == "success"){
const credential = initFirebase.auth.FacebookAuthProvider.credential(token);
initFirebase.auth().signInWithCredential(credential)
.catch(err => {
console.log(err)
})
}
}
i am using appid in strings but i have not added it here hope you understand that.
and the error is this
this is my firebase config file code
import firebase from "firebase/app"
import "firebase/firestore"
import "firebase/auth"
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "xxxxxxxx",
authDomain: "xxxxxx",
databaseURL: "xxxxxx",
projectId: "xxxx",
storageBucket: "xxxxx",
messagingSenderId: "xxxxx",
appId: "xxxxx"
};
// Initialize Firebase
const initFirebase = firebase.initializeApp(firebaseConfig);
export default initFirebase
variable values are hidden because of privacy .i worked with firestore and that worked but i am seeing issue with auth with facebook .
i am using react native , firebase , expo and not firebase sdk
You don't have to register a Facebook App for Login with Facebook using Firebase. You can use the below code with Firebase Facebook Authentication enabled in the console.
export const loginWithFacebook = (accessToken) => {
const credential = initFirebase.auth.FacebookAuthProvider.credential(accessToken);
return new Promise((resolve, _reject) => {
signInWithCredential(credential).then(response => {
resolve(response);
});
});
};

Connect Firebase with React Native

I'm trying to connect React Native with Firebase. I'm using the following method to connect Firebase.
import * as firebase from 'firebase';
// Initialize Firebase
const firebaseConfig = {
apiKey: "<YOUR-API-KEY>",
authDomain: "<YOUR-AUTH-DOMAIN>",
databaseURL: "<YOUR-DATABASE-URL>",
storageBucket: "<YOUR-STORAGE-BUCKET>"
};
firebase.initializeApp(firebaseConfig);
app.authWithPassword({
"email": "abc#abc.com",
"password": "abc1234"
}, (error, user_data) => {
if (error) {
alert('Login Failed. Please try again');
} else {
AsyncStorage.setItem('user_data', JSON.stringify(user_data));
}
});
But it returns the following error:
app.authWithPassword is not a function.
I'm using Firebase 3.4 and React Native 0.32.
From the Firebase documentation:
firebase.auth().signInWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});

Resources