Below is the older version to read data from firestore
db.collection('cafes').get().then(snapshot => {
snapshot.docs.forEach(doc => {
console.log(docs)
});
});
I think that's why I am getting db.collection() is not a function.
But from the present docs, it is like
async function getcafes(db) {
const cafesCol = collection(db, 'cafes');
const cafeSnapshot = await getDocs(cafesCol);
const cafeList = cafeSnapshot.docs.map(doc => doc.data());
console.log(cafeList)
return cafeList;
}
So while using console.log(), it is not giving any output.
<head>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
import {
getFirestore,
collection,
getDocs,
} from "https://www.gstatic.com/firebasejs/9.6.1/firebase-firestore.js";
const firebaseConfig = {
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const analytics = getAnalytics(app);
window.db = db; // to make db global variable
async function getcafes(db) {
console.log('Fetching Cafes');
const cafesCol = collection(db, "cafes");
const cafeSnapshot = await getDocs(cafesCol);
const cafeList = cafeSnapshot.docs.map((doc) => doc.data());
console.log(cafeList);
return cafeList;
}
getcafes() // calling the function
</script>
The issue is that within getcafes(), db is undefined, as db within the function is a local parameter. Try removing the db parameter from getcafes and allowing it to use the global window.db.
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const analytics = getAnalytics(app);
window.db = db; // to make db global variable
// remove db parameter VV
async function getcafes() {
console.log("Fetching Cafes");
// now db here is the global database instead of undefined
const cafesCol = collection(db, "cafes");
const cafeSnapshot = await getDocs(cafesCol);
const cafeList = cafeSnapshot.docs.map((doc) => doc.data());
console.log(cafeList);
return cafeList;
}
getcafes(); // calling the function
Related
I am also using getStaticprops from next.js.
I am trying to fetch data , I am following the code on firebase documentation, but i keep running into a error
export const getStaticProps = async () => {
const res = await fetch(
"https://jsonplaceholder.typicode.com/photos?_limit=4"
);
const images = await res.json();
console.log(auth.currentUser);
//
const userRef = ref(database, "users/" + auth.currentUser.uid);
onValue(userRef, (snapshot) => {
const properties = snapshot.val();
console.log(properties);
});
//
return {
props: {
images,
properties,
},
};
};
I'm trying to interact with the database in a function, but I'm getting an error when trying to use the doc() function within a firebase function
The error im getting is: " Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app).
"
See the code below and the function I'm getting the error on.
Im guessing in has something to do with not being able to use the db variable? But if I cant do that, how do I interact with the database?
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import {
collectionGroup, doc, getDoc,
getDocs,
getFirestore,
query,
} from "firebase/firestore";
// Sendgrid Config
import sgMail from "#sendgrid/mail";
import {
ClientUserData,
Course,
EventType,
Task,
} from "../../src/views/types/interfaces";
import {startOfDay} from "date-fns";
const adminApp = admin.initializeApp();
const auth = adminApp.auth();
const db = getFirestore();
//THE FUNCTION IT FAILS ON
export const checkCourseForNewClientProgram = functions.pubsub.schedule("Every day").onRun(async () => {
const courses: Course[] = [];
const coursesByCompanies = query(collectionGroup(db, "courses"));
// eslint-disable-next-line max-len
const checkCourseForNewClientProgramSnapshot = await getDocs(coursesByCompanies);
checkCourseForNewClientProgramSnapshot.forEach((courseSnapshot) => {
const course = courseSnapshot.data() as Course;
if (course.events && course.events.length > 0) {
const courseEvents = course.events;
const todayDate = startOfDay(new Date()).getTime();
courseEvents.forEach((event) => {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
// eslint-disable-next-line max-len
const eventStart = convertFirebaseDateIntoJSDate(event.start.seconds).getTime();
if (todayDate === eventStart) {
courses.push(course);
}
if (event.type === EventType.TASK) {
const eventLessons = (event as Task).lessons;
if (eventLessons && eventLessons.length > 0) {
eventLessons.forEach((eventLesson) => {
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
// #ts-ignore
// eslint-disable-next-line max-len
const lessonStart = convertFirebaseDateIntoJSDate(eventLesson.start.seconds).getTime();
if (todayDate === lessonStart) {
courses.push(course);
}
});
}
}
});
}
});
let userIds: string[] = [];
courses.forEach((course) => {
if (course.users) {
userIds = course.users.map((userId) => userId);
}
});
const userEmails = await Promise.all(
userIds.map(async (userId) => {
// eslint-disable-next-line max-len
const userData = await getDocumentFromId<ClientUserData>("company_clients", userId);
return userData.email;
})
);
await Promise.all(
userEmails.map(async (userEmail) => {
const msg = {
to: userEmail,
from: "nicky#byplayart.dk",
templateId: "d-8609d087e96e42d5abe6991d19afb22d",
dynamic_template_data: {
email: userEmail,
toName: "Testing tasks",
fromName: "Unlimited Performance",
subject: "Du har en opgave der venter",
text: "Dette er din tekst",
},
};
try {
await sgMail.send(msg);
return {
success: true,
};
} catch (e) {
return {
success: false,
error: e,
};
}
})
);
});
With the Admin SDK version 9 you should use the namespaced syntax, similarly to the Client SDK v8. The corresponding reference for the Admin DSK is actually the Node.js one.
Your code should then been adapted as follows:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
const db = admin.firestore();
export const checkCourseForNewClientProgram = functions.pubsub.schedule("Every day").onRun(async () => {});
const coursesByCompanies = db.collectionGroup("courses");
const checkCourseForNewClientProgramSnapshot = await coursesByCompanies.get();
checkCourseForNewClientProgramSnapshot.forEach((courseSnapshot) => {...});
//...
return null;
});
Don't forget to correctly terminate your Cloud Function, either by returning the Promise returned by the last Promise.all() call or a value (e.g. return null;) after all the asynchronous work is complete. This is a key point and I would suggest you read this part of the doc and watch the 3 videos about "JavaScript Promises" from the Firebase video series.
I'm trying to load data in to Firestore using firebase functions from the function storeCategories(), it currently just runs on start but I plan to schedule it once I have it working.
This function works fine on the emulators but in production I'm getting the either 'socket hang up' or 'Client network socket disconnected before secure TLS connection was established'
storeTrending() works fine so I'm not sure what is causing it.
/** #format */
const functions = require("firebase-functions");
const cors = require("cors");
const express = require("express");
const fetch = require("node-fetch");
const apiKey = functions.config().tmdbapi.key;
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
var app = express();
app.use(cors());
const storeCategories = async () => {
try {
// GET CATEGORY IDS
const categoriesIDRes = await fetch(
`https://api.themoviedb.org/3/genre/movie/list?api_key=${apiKey}`
);
const categoriesInfo = await categoriesIDRes.json();
// CREATE CATEGORY API LINKS
const categoriesURLs = categoriesInfo.genres.map(
(el) =>
`https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&language=en-US&sort_by=popularity.desc&include_adult=true&include_video=true&page=1&with_genres=${el.id}&with_watch_monetization_types=flatrate`
);
// RETRIEVE POPULAR FILMS FROM EACH CATEGORY
const categoriesRes = await Promise.all(
categoriesURLs.map((el) => fetch(el))
);
const data = await Promise.all(
categoriesRes.map((el) => el.json())
);
const batch = db.batch();
// WRITE TO FIRESTORE FOR FASTER INITIAL LOAD
data.forEach((category, idx) => {
const ref = db
.collection("categories")
.doc(categoriesInfo.genres[idx].name);
batch.set(ref, category);
});
const categoriesInfoRef = db.collection("categoryIDs").doc("IDs");
batch.set(categoriesInfoRef, categoriesInfo);
return batch.commit();
} catch (err) {
console.log(err);
return;
}
};
storeCategories();
const storeTrending = async () => {
try {
const res = await fetch(
`https://api.themoviedb.org/3/trending/all/week?api_key=${apiKey}`
);
const data = await res.json();
db.collection("categories").doc("trending").set(data);
} catch (err) {
console.log(err);
return;
}
};
storeTrending();
Any help with this would be greatly appreciated!
Edit: screenshot of the error
I am trying to use a Firebase callable function to create a Twilio token for a React project. The project should allow video calls using Twilio's webRTC service. The code is based on the example here https://www.twilio.com/blog/video-chat-react-hooks. I am attempting to move the server code to obtain the Twilio token for the video service into the Firebase callable function.
My console gives the following output and errors:
error check 1: handleSubmit called
error check 2: getToken function retrieved from functions ƒ (r){return n.call(e,r,t||{})}
error check 4: token set
POST https://us-central1-vid-chat-app.cloudfunctions.net/getToken 500
Uncaught (in promise) Error: INTERNAL
at new m (error.ts:66)
at b (error.ts:175)
at P.<anonymous> (service.ts:244)
at tslib.es6.js:100
at Object.next (tslib.es6.js:82)
at a (tslib.es6.js:71)
Here is the error from the Firebase console
Unhandled error Error: accountSid is required
at new AccessToken (/srv/node_modules/twilio/lib/jwt/AccessToken.js:213:28)
at generateToken (/srv/index.js:9:12)
at videoToken (/srv/index.js:23:19)
at exports.getToken.functions.https.onCall (/srv/index.js:42:19)
at func (/srv/node_modules/firebase-functions/lib/providers/https.js:273:32)
at corsHandler (/srv/node_modules/firebase-functions/lib/providers/https.js:293:44)
at cors (/srv/node_modules/cors/lib/index.js:188:7)
at /srv/node_modules/cors/lib/index.js:224:17
at originCallback (/srv/node_modules/cors/lib/index.js:214:15)
at /srv/node_modules/cors/lib/index.js:219:13
These are the two main files I believe are relevant
React file
import React, { useState, useCallback } from 'react';
import firebase from './firebase'
import Lobby from './Lobby';
import Room from './Room';
const VideoChat = () => {
const [username, setUsername] = useState('');
const [roomName, setRoomName] = useState('');
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback(event => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback(event => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(async event => {
console.log("error check 1: handleSubmit called")
event.preventDefault();
const getToken = firebase.functions().httpsCallable('getToken')
console.log("error check 2: getToken function retrieved from functions", getToken)
getToken({ identity: username, room: roomName })
.then(result => {
console.log("error check 3: calling .then")
setToken(result.data)
})
console.log("error check 4: token set")
}, [username, roomName]);
const handleLogout = useCallback(event => {
setToken(null);
}, []);
let render;
if (token) {
render = (
<Room roomName={roomName} token={token} handleLogout={handleLogout} />
);
} else {
render = (
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
);
}
return render;
};
export default VideoChat;
Firebase function
const twilio = require("twilio");
const functions = require('firebase-functions');
const config = require('./config');
const AccessToken = twilio.jwt.AccessToken;
const { VideoGrant } = AccessToken;
const generateToken = config => {
return new AccessToken(
config.twilio.accountSid,
config.twilio.apiKey,
config.twilio.apiSecret
);
};
const videoToken = (identity, room, config) => {
let videoGrant;
if (typeof room !== "undefined") {
videoGrant = new VideoGrant({ room });
} else {
videoGrant = new VideoGrant();
}
const token = generateToken(config);
token.addGrant(videoGrant);
token.identity = identity;
return token;
};
const sendTokenResponse = (token, res) => {
res.set('Content-Type', 'application/json');
res.send(
JSON.stringify({
token: token.toJwt()
})
);
};
exports.getToken = functions.https.onCall((data, context)=>{
console.log('Peter error check getToken function called')
const identity = data.identity;
const room = data.room;
const token = videoToken(identity, room, config);
sendTokenResponse(token, res);
console.log('here is the token', token)
return token
});
Config file also contained in Firebase cloud function folder
module.exports = {
twilio: {
accountSid: process.env.TWILIO_ACCOUNT_SID,
apiKey: process.env.TWILIO_API_KEY,
apiSecret: process.env.TWILIO_API_SECRET,
chatService: process.env.TWILIO_CHAT_SERVICE_SID,
outgoingApplicationSid: process.env.TWILIO_TWIML_APP_SID,
incomingAllow: process.env.TWILIO_ALLOW_INCOMING_CALLS === "true"
}
};
my approach to do this is:
1) Environment Configuration: store your environment data.
firebase functions:config:set twilio.sid="The Account SID" twilio.apikey="The api key" twilio.apisecret="The api secret"
2) Firebase Function: use https.onCall
const functions = require('firebase-functions');
const AccessToken = require('twilio').jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
*//call environment data*
const accountSid = functions.config().twilio.accountsid;
const apiKey = functions.config().twilio.apikey;
const apiSecret = functions.config().twilio.secret;
*//function to generate token*
const generateToken = () => {
return new AccessToken(accountSid, apiKey, apiSecret);
};
*//function to generate video grant*
const videoToken = (identity, room) => {
videoGrant = new VideoGrant({ room });
const token = generateToken();
token.addGrant(videoGrant);
token.identity = identity;
return token;
};
*//firebase function*
exports.twilio = functions.https.onCall((data, context) => {
const identity = data.identity;
const room = data.room;
const token = videoToken(identity, room);
return token.toJwt();
}});
3) React code:
const handleSubmit = useCallback( async () => {
try {
const twilioRequest = firebase
.functions()
.httpsCallable('twilio');
const response = await twilioRequest({
identity: username,
room: roomName,
});
setToken(response.data);
} catch (err) {
console.log(err);
}
},[username, roomName]);
4) Allowing unauthenticated HTTP function invocation
Be sure you have permissions granted https://cloud.google.com/functions/docs/securing/managing-access-iam#allowing_unauthenticated_function_invocation
The "INDEX" sheet is created and has all its data and the sheets titled with the "product code" are created but completely blank. The console log function inside the inner foreach loop does print the data on each iteration but yet the insertRow function wont populate the sheets with the data. Can someone plz tell me what I'm doing wrong.
index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const fs = require('fs-extra')
const gcs = require('#google-cloud/storage')();
const path = require('path');
const os = require('os');
const ExcelJS = require('exceljs');
exports.createCSV = functions.firestore
.document('reports/{reportId}')
.onCreate(async (snap, context) => {
// Step 1. Set main variables
console.log("Began");
const reportId = context.params.reportId;
const fileName = `reports/${reportId}.xlsx`;
console.log(fileName);
const tempFilePath = path.join(os.tmpdir(), fileName);
// Reference report in Firestore
const db = admin.firestore();
const reportRef = db.collection('reports').doc(reportId);
// Reference Storage Bucket
const storage = gcs.bucket('gs://stock-check-48f78.appspot.com');
const workbook = new ExcelJS.Workbook();
// Step 2. Query collection
try {
const querySnapshot = await db.collection('stores')
.get();
// create array of order data
const indexWorksheet = workbook.addWorksheet("INDEX");
querySnapshot.forEach(async doc => {
//stores.push(doc.data());
const rowValues = [];
rowValues[1] = doc.data().code.toString();
rowValues[2] = doc.data().name;
rowValues[3] = doc.data().address;
indexWorksheet.addRow(rowValues);
const worksheet = workbook.addWorksheet(doc.data().code.toString());
const storeId = doc.id;
console.log("Store id is: " + storeId);
const querySnap = await db.collection('stores').doc(storeId).collection('products').get();
querySnap.forEach(async a => {
//console.log(a.data());
const productValues = [];
productValues[1] = a.data().name;
productValues[2] = a.data().packaging;
productValues[3] = a.data().category;
productValues[4] = a.data().stock.toString();
productValues[5] = a.data().order.toString();
productValues[6] = a.data().last_updated;
worksheet.insertRow(1, productValues);
});
});
// Step 4. Write the file to cloud function tmp storage
console.log("Filename is: ");
console.log(fileName);
console.log(tempFilePath);
const buffer = await workbook.xlsx.writeBuffer();
await fs.outputFile(tempFilePath, buffer);
console.log("Uploaded");
// Step 5. Upload the file to Firebase cloud storage
const file = await storage.upload(tempFilePath, {
destination: fileName
});
console.log("Uploaded to bucket");
return reportRef.update({
status: 'complete'
});
} catch (err) {
return console.log(err);
}
})
Update
I made these changes and still get the same result...(see edited code above)
Try using promise.all
await Promise.all(querySnap.map(async a => {
//console.log(a.data());
const productValues = [];
productValues[1] = a.data().name;
productValues[2] = a.data().packaging;
productValues[3] = a.data().category;
productValues[4] = a.data().stock.toString();
productValues[5] = a.data().order.toString();
productValues[6] = a.data().last_updated;
worksheet.insertRow(1, productValues);
}));