Firebase function works when deployed but not locally - node.js

I have a function that just fetches the data from my firebase and displays it. This works perfectly when deployed, but not locally.
I've attached my code just in case, but seeing as it works when deployed, I dont think that will be the problem, also its copy pasted from freecodecamp tutorial.
Directory is as follows:
firebase folder
|functions
||APIs
|||todos.js
||util
|||admin.js
||index.js
Also, the local version does have an output, its just the empty array initialised in todos.js line 9.
//todos.js
const { db } = require('complete file path');
exports.getAllTodos = (request, response) => {
db
.collection('todos')
.orderBy('createdAt', 'desc')
.get()
.then((data) => {
let todos = [];
data.forEach((doc) => {
todos.push({
todoId: doc.id,
title: doc.data().title,
body: doc.data().body,
createdAt: doc.data().createdAt,
});
});
return response.json(todos);
})
.catch((err) => {
console.error(err);
return response.status(500).json({ error: err.code});
});
};
//admin.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
module.exports = { admin, db };
//index.js
const functions = require('firebase-functions');
const app = require('express')();
const {
getAllTodos
} = require('./APIs/todos')
app.get('/todos', getAllTodos);
exports.api = functions.https.onRequest(app);
I also performed export GOOGLE_APPLICATION_CREDENTIALS="path/to/key.json" to no avail.

You initialized the the app without any credentials:
const refreshToken; // Get refresh token from OAuth2 flow
admin.initializeApp({
credential: admin.credential.refreshToken(refreshToken),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
[Reference this site for more information:] https://firebase.google.com/docs/admin/setup/#initialize-without-parameters

Related

How to get data from both Authentication and firestore at same time?

I am doing a firebase project, with Node JS admin SDK
In my firebase:
In Authentication->users->(list of users with information phoneNumber, signedInDate, User UID)
In Firestore Databases->users(collection)->name, email
I want to read the data and display it as table,
i.e., phone, user UID,name,email in a table
I have written code like this, until now:
const admin = require('firebase-admin');
const serviceAccount = require("C:/Users/santo/Downloads/adminsdk.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let fs = admin.firestore();
let auth = admin.auth();
const listAllUsers = async (nextPageToken) => {
try {
let result = await auth.listUsers(100, nextPageToken);
result.users.forEach((userRecord) => {
console.table(userRecord.toJSON().uid);
console.table(userRecord.toJSON().phoneNumber);
});
if (result.pageToken) {
listAllUsers(result.pageToken);
}
} catch(ex) {
console.log('Exception listing users:', ex.message);
}
}
async function start() {
const santosh = await fs.collection('users').doc('ANCyBKH2z5jKm1xZx6vegFUr2').get();
//console.table([santosh.data()]);
await listAllUsers();
}
start();

Firebase Functions Error, "db.batch() is not a function"

I've been having an issue with Firebase functions where when I deploy this function and try to change a user image, I get an error in the firebase function logs that says "Error, db.batch is not a function".
exports.onUserImageChange = functions
.firestore.document('/users/{userId}')
.onUpdate((change) => {
console.log(change.before.data());
console.log(change.after.data());
if (change.before.data().imageUrl !== change.after.data().imageUrl) {
console.log('image has changed');
var batch = db.batch();
return db
.collection('screams')
.where('userHandle', '==', change.before.data().handle)
.get()
.then((data) => {
data.forEach((doc) => {
const scream = db.doc(`/screams/${doc.id}`);
batch.update(scream, { userImage: change.after.data().imageUrl });
});
return batch.commit();
});
} else return true;
});
This is my db export:
const admin = require('firebase-admin')
admin.initializeApp();
const db = admin.firestore();
module.exports = { admin, db};
I guess the issue is that you have either not imported db correctly or you might have forgotten to assign the db variable before calling it.

How to send data via HTTP request to Firestore database

So, I am trying to make an app with Node.js, Vue, and Firebase. I currently have a very basic function using firebase functions to send data to the realtime database via http request. I cant figure out how to do the same for Firestore database.
My goal is to send data or a file of some JSON data to my Firestore database from outside the app with an HTTP request. I know how to deploy functions I just am not sure how to send files or JSON to the database for further use.
Any help is greatly appreciated!
I have been playing around with different functions but can't seem to figure it out. I have looked at many of the docs/videos for firebase but I am pretty knew to this and can't quite grasp it.
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp() //need admin sdk
const toUpperCase = (string) => string.toUpperCase();
exports.addMessage = functions.https.onRequest((request, response) => {
const text = request.query.text;
const secretText = toUpperCase(text);
admin
.database()
.ref('/sentMSG')
.push({ text: secretText })
.then(() =>
response.json({
message: 'great!!!!',
text
})
)
.catch(() => {
response.json({
message: 'not great :^('
});
});
});
exports.writeToStore = functions.firestore.https.onRequest((request,
response) => {
let data = request.query.text
let setDoc = db.collection('cities').doc('LA').set(data)
.then(() =>
response.json({
message: 'great!!!!',
text
})
)
.catch(() => {
response.json({
message: 'not great :^('
});
});
});
The addMessage function adds data to realtime database but I need to do it for Firestore
index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const _ = require('lodash');
admin.initializeApp();
const db = admin.firestore();
const express = require('express');
const cors = require('cors');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.get('/', async (req, res) => {
let data = req.query.data;
try {
await db.collection('users').doc().set({ userId: data });
} catch(err) { res.send(JSON.stringify(err)) }
res.send('Success');
})
app.post('/', async (req, res) => {
let payload = req.body;
let keys = Object.keys(payload);
let obj = {};
let i = 0;
try {
_.forEach(payload, async data => {
obj[keys[i]] = data;
i++;
})
await db.collection('users').doc().set(obj);
} catch(err) { res.send(JSON.stringify(err))}
res.send('Success');
})
exports.writeToFirestore = functions.https.onRequest(app);
You can then call this Cloud Function like such: linkToURL/writeToFirestore?data=5 or make a Post request to linkURL/writeToFirestore
From what I understand, whenever the Cloud Function is triggered, you want to write data in both the Firebase Realtime Database and in Firestore. Cloud Function will only execute one function in the exports object. In this case, since the function is being triggered by HTTPS, you can create a single exports.writeToBoth that encapsulates the code to write in Realtime DB and Firestore. Furthermore, I noticed that the writeToFirestore written is invalid, you need to change functions.firestore.https.onRequest to functions.https.onRequest.
In any case, you can refer to this code as a base, should you want:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
let writeToFirestore = data => {
let setDoc = db.collection('users').doc('0').set(data);
}
let writeToRealtimeDB = data => {
// ...
// Your code
// ...
}
exports.writeToBoth = functions.https.onRequest((req, res) => {
let data = req.query.text;
// Write to both DB
try {
writeToFirestore(data);
writeToRealtimeDB(data);
} catch(err) { res.send('Error') }
res.send('Success');
})

Firebase admin database always returns null as spnapshot value

Following this instructions I'm always getting null for snapshot.val();. Why is that?
I'm using node sdk for accessing users collection on firebase database. I'm sure the collection does exist:
Requesting users:
import { NextFunction, Request, Response } from 'express';
import * as admin from 'firebase-admin';
export const getUsers = (request: Request, response: Response, next: NextFunction) => {
admin.database.enableLogging(true);
const db = admin.database();
const ref = db.ref("users");
ref.once("value", function (snapshot) {
const users = snapshot.val();
console.log('users', users); // always null
response.json({
success: true,
result: users
});
});
}
Setting up admin:
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: config.databaseURL
});
Node version:
v12.4.0
Here is a log from node:
Seems that I missed the main part related to Firebase Realtime Databases. There're two types of them: Realtime Database and Cloud Firestore (The next generation of the Realtime Database). As I'm using the second one, I need to use appropriate namespace of firebase-admin, which should be firestore (not database, which is for Realtime Database - the first one):
const db = admin.firestore();
const userRef = db.collection('users');
userRef.get()
.then(snapshot => {
const users: any = [];
snapshot.forEach(doc => {
users.push(doc.data());
});
response.json({
success: true,
users: users
});
})
.catch(err => {
console.log('err', err);
});

Firebase function error <admin.auth is not a function at ..>

I'm fairly new to Firebase and Node.js. I have created this function in my Cloud Functions to login users with a custom token:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var serviceAccount = require("./service-account.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: functions.config().firebase.databaseURL
});
const cors = require('cors')({origin: true});
exports.login = functions.https.onRequest((req, res) => {
cors(req, res, () => {
//doing some validation..
//get password from db and match it with input password
var userRef = admin.firestore().collection('users')
userRef.where('username', '==', username).get()
.then(snapshot => {
if(snapshot.size > 1){
res.status(200).send("Invalid account!");
return;
}
snapshot.forEach(doc => {
var userPass = doc.data().password;
//if password matches, generate token, save it in db and send it
if(userPass && password == userPass){
var uid = doc.data().uid;
var admin = Boolean(doc.data().admin);
var server = Boolean(doc.data().server);
var additionalClaims = {
admin: admin,
server: server
};
admin.auth().createCustomToken(uid, additionalClaims)
.then(function(customToken) {
res.status(200).send("token:" + customToken);
})
.catch(function(error) {
res.status(200).send("Token creation failed!");
});
//save token in db..
}else{
res.status(200).send("Invalid credentials!");
}
});
})
.catch(err => {
res.status(200).send("User authentication failed!");
});
});
});
I used the token generation method in the documentation, but whenever I try to login a user it throws the error:
TypeError: admin.auth is not a function
at snapshot.forEach.doc (/user_code/index.js:128:27)
at QuerySnapshot.forEach (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/reference.js:1012:16)
at userRef.where.get.then.snapshot (/user_code/index.js:110:13)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
What could it be that I'm doing wrong?
This declaration of admin:
var admin = Boolean(doc.data().admin);
is hiding this one:
const admin = require('firebase-admin');
Use a different name, such as:
var docAdmin = Boolean(doc.data().admin);

Resources