I am trying to create device to device push notifications for an iOS app using Firebase Cloud functions. I want to trigger an event whenever a new child is created in database at reference '/user-notifications/{notificationRecipientUid}/{challengeId}'. Here is my index.js code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendWinLoseOrTieNotification = functions.database.ref('/user-notifications/{notificationRecipientUid}/{challengeId}').onWrite(event => {
...
const challengeId = event.params.challengeId;
functions.database.ref('/challenges/' + challengeId).once('value').then(function(snapshot) {
const challengerUid = snapshot.val().challengerUid;
...
});
});
When a new child is added in the database at that location, I get this error, "TypeError: functions.database.ref(...).once is not a function", in Firebase Console's Functions Logs. So there is no 'once' method available
on ref like in web api:
firebase.database().ref('...').once('value').then(function(snapshot) {
...
});
My question is: How to read an existing database value inside index.js?
Well, the solution is to use admin instead of firebase like so:
admin.database().ref('...').once('value').then(function(snapshot) {
...
});
Related
Hi I'd like to create the following document with the private subcollection, via a cloud function trigger:
This is what I have so far, but have no idea how to actually set a subcollection as well.
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
// Cloud Functions for Firebase SDK to create Cloud Functions + set up triggers.
const functions = require("firebase-functions");
// The Firebase Admin SDK to access Firestore.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
// Listen to .onCreate trigger
exports.createProfile = functions.auth.user().onCreate((user) => {
return admin.firestore().doc(`/users/${user.uid}`).set({
firstName: null,
lastName: null,
preferredName: null,
});
});
A reference to a doc can be formed by alternating collection and doc references. Going a little overboard to spell it out...
const usersCollectionRef = firestore().collection('users')
const userRef = usersCollectionRef.doc(user.uid);
const privatesCollectionRef = userRef.collection('private');
const accountRef = privatesCollectionRef.doc('account');
return accountRef.set(...)
// or, the other extreme lines-of-code-wise
const accountRef = firestore().doc(`/users/${user.uid}/private/account`)
return accountRef.set(...)
Note that you'll only be able to have one doc at a time named "account" in the "private" collection. Not sure about the semantics of your app, but the user's email inside that doc might be a better doc name.
I have 2 collections in my firestore (global and local) and when I add a doc to local I need to update a field in the global doc by 1
Below is the code I have for the same. I am very new to this so I might have some syntactical error as well, please do highlight if you find any.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello world");
}); // For testing, even this is not being deployed
exports.updateGlobal = functions.firestore
.document("/local/{id}")
.onCreate((snapshot, context) => {
console.log(snapshot.data());
return admin
.firebase()
.doc("global/{id}")
.update({
total: admin.firestore.FieldValue.increment(1),
});
});
The Terminal says "function failed on loading user code"
Before this, it showed something along the lines of "admin is undefined" or "cannot access firestore of undefined" which I'm unable to replicate now.
This is a part of a react app which has normal firestore working through firebase npm module
Any other info needed regarding the issue I'll edit the question accordingly, thank you so much for the help.
In addition to loading the firebase-functions and firebase-admin modules, you need to initialize an admin app instance from which Cloud Firestore changes can be made, as follows:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//...
I see another problem in your CF. You need to use the context object to get the value of id.
exports.updateGlobal = functions.firestore
.document("/local/{id}")
.onCreate((snapshot, context) => {
const docId = context.params.id;
return admin
.firebase()
.doc("global/" + docId)
.update({
total: admin.firestore.FieldValue.increment(1),
});
});
You can also use template literals as follows:
return admin
.firebase()
.doc(`global/${docId}`)
//...
When using const db = firebase.database(), does it matter where I declare this in a cloud function script?
For example, index.ts which contains all of my cloud functions, should I declare it at the top, or in each individual function?
const db = firebase.database()
export const functionA = functions.https.onCall(async (data, context) => {
// use db here
});
export const functionB = functions.https.onCall(async (data, context) => {
// use db here
});
OR
export const functionA = functions.https.onCall(async (data, context) => {
const db = firebase.database()
});
export const functionB = functions.https.onCall(async (data, context) => {
const db = firebase.database()
});
Or does this not matter?
The first approach creates the db instance when the code loads.
The second approach creates the db instance when the code runs.
Neither is pertinently better than the other, and the firebase.database() operation is very lightweight so it's likely to make little difference in practice.
What does make a difference is whether you load the database SDK to begin with. Some of the SDKs in Firebase are quite big, and not each Cloud Function needs all SDKs. So a common trick to speed up load/cold-start times is to move the require('firebase-...') statements into the body of the Cloud Function(s) that require them.
Normally, if you want to interact, from a Cloud Function, with the Realtime Database you just need to initialize the Admin SDK and get the Database service for the default app (or a given app), as explained here in the doc.
So you would do something like:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Get the Database service for the default app
const db = admin.database();
To get more details on what Frank explains about Cold Start in his answer, you should read the following article: https://medium.com/firebase-developers/organize-cloud-functions-for-max-cold-start-performance-and-readability-with-typescript-and-9261ee8450f0
However in your case, since your two Cloud Functions use the Admin SDK, there shouldn't be any difference between the two approaches presented in your question, as Frank mentioned.
I'm trying to deploy a cloud function that will trigger whenever a document is added to a particular collection as below:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const Firestore = require("#google-cloud/firestore");
const firestore = new Firestore({ projectId: config.projectId });
admin.initializeApp(config);
exports.checkCapacity = functions.firestore.document("gran_canaria/las_palmas_1/miller_pendientes/{albnum}")
.onCreate(async (snapshot, context) => {});
However this throws the Deployment failure error:
Failed to configure trigger providers/cloud.firestore/eventTypes/document.create#firestore.googleapis.com (gcf.us-central1.checkCapacity)
The error clears if I remove the wildcard and change the reference to:
"gran_canaria/las_palmas_1/miller_pendientes/albnum"
I've attempted changing the method to onWrite(), deleting and re-deploying the function and checking the cloud status at https://status.cloud.google.com/ but can't find any solutions.
I have been able to deploy successfully a Cloud Function with a trigger on an onCreate event on my Cloud Firestore.
I have been successful by imply using the provided template in the Console UI when creating the Cloud with the following:
The index.js used is the sample provided by GCP when created the function, which simply prints to the logs which document triggered the change.
Looking at the documentation in Firestore, I see that you probably used the samples provided there, so maybe using the above settings will make it work for you.
Here is how my Firebase Schema is laid out:
I am able to index everything except _geoloc: into my Algolia Index with this code:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Authenticate to Algolia Database.
// TODO: Make sure you configure the `algolia.app_id` and `algolia.api_key` Google Cloud environment variables.
const algoliasearch = require('algoliasearch');
const client = algoliasearch(functions.config().algolia.app_id, functions.config().algolia.api_key);
// Name fo the algolia index for content.
const ALGOLIA_POSTS_INDEX_NAME = 'businessSearch';
exports.indexentry = functions.database.ref('/businessSearch/{uid}/').onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
const firebaseObject = Object.assign({}, event.data.val(), {
functions.database.ref('/businessSearch/{uid}/_geoloc').onWrite(event =>{
})
objectID: event.params.uid,
});
index.saveObject(firebaseObject); // .then or .catch as well
index.addObject
});
How can I index the _geoloc: child node into my Algolia Index with Node.js?
I heard somewhere that this is not possible to index a nested object with Object.assign, but I just wanted to see if it was.
There is a timing issue when the database is written and what gets indexed by Algolia. The code included in my question does work with nested objects.