Google cloud functions Firebase Update FieldValue.increment(1) using NodeJS - TypeError: FieldValue.increment is not a function - node.js

Im using NodeJs within google cloud functions and I want to increase the value of one parameter in one document
I initiate Firestore:
const Firestore = require('#google-cloud/firestore');
const PROJECTID = 'XXXX';
const firestore = new Firestore({
projectId: PROJECTID,
timestampsInSnapshots: true
});
And my .get() and .set() functions work correctly. But when I try to update a value incrementing it, I get errors with FieldValue.increment
I've tried:
await snapshot.ref.update({ parameter: FieldValue.increment(1) });
adding:
const FieldValue = require('firebase-admin').firestore.FieldValue;
await snapshot.ref.update({ parameter: FieldValue.increment(1) });
as explained in other sites, and:
documentRef.update(
'parameter', Firestore.FieldValue.increment(1)
)
as explained in https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/fieldvalue
const admin = require('firebase-admin');
increment = admin.firestore.FieldValue.increment(1);
await snapshotRef.update({ parameter: increment });
But none work.
Error:
TypeError: admin.firestore.FieldValue.increment is not a function
or
TypeError: FieldValue.increment is not a function
"dependencies": {
"firebase-admin": "^6.5.1",
"nodemailer": "^6.6.1"
}

The increment() was added to Firestore SDK in April 2019 as mentioned in the release notes but Firebase Admin 6.5.1 was released in January 2019. Upgrading to latest version should resolved this issue.

Just posting this as a community wiki. Command to update to the latest Firebase Admin version.
npm i firebase-admin#latest
You could also modify the specific package in package.json by changing the version to the latest. See sample below:
"firebase-admin": "latest"
and then run
npm install
Also, for best practice, always remove the node_modules folder and package-lock.json and then run npm install before you deploy your app.

Related

Firestore simple node script on pc doesn't work even though same code runs perfectly on firebase functions

Hello I am running a small script that I want to run locally since max timeout of firebase functions is 9 minutes and that is not enough for me (I have to run a large scale update on data types).
So the code is basically:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
db.collection('users')
.get()
.then(querySnapshot => {
querySnapshot.docs.forEach(doc => {
// update doc
});
});
But querySnapshot.docs has 0 elements. I checked the same code in functions and it works properly. What could be the cause of this? If this is not possible are there any workarounds where I can bypass timeout using cloud functions?
Firebase is initialized correctly both in my machine and directory. I tried a clean initialized directory too. Same code when passed to an firebase function endpoint and ran once works perfectly fine.
If you run a script written with the Admin SDK locally on your computer you need to initialize the SDK with an exported service account key file as explained in the doc.
The doc details how to:
Generate a private key file in JSON format from the Firebase console
Set an environment variable to the file path of the JSON file that contains your service account key.
Then you can do as follows:
const admin = require('firebase-admin');
admin.initializeApp({
credential: applicationDefault()
});
const db = admin.firestore();
db.collection('users')
.get()
.then(querySnapshot => {
return Promise.all(querySnapshot.docs.map(doc => doc.ref.update( {...} ))):
})
.then(() => {...})
.catch(...)
Note that to update several docs within a loop via the asynchronous update() method you'll need to use Promise.all(), as shown above.
When I initialized like this everything seemed to work fine. The reason why applicationDefault doesn't work is I think because doc says it works in google environments.
const admin = require('firebase-admin');
var serviceAccount = require('path/to/key.json')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});

Property 'register' does not exist on type 'NestExpressApplication'

According to nestjs documentation I want to use fastify-helmet in order to config contentSecurityPolicy.
first I installed the following package:
$ npm i --save fastify-helmet
in my main.ts I registered fastifyHelmet but it throw the following error :
Property 'register' does not exist on type 'NestExpressApplication'
async function bootstrap() {
const logger = new Logger('bootstrap')
const app = await NestFactory.create<NestExpressApplication>(AppModule);
await app.register(fastifyHelmet); // this line thorw error
...
const PORT = process.env.PORT
await app.listen(PORT);
logger.log(`Application is start on port : ${PORT}`)
}
bootstrap();
The problem is that you are trying to register a fastify package to your express based app which does not work.
There are 2 possibilities to fix your issue:
Use a express compatible package (for example helmet: npm i --save helmet) and then register it with: app.use(helmet) (express does not use .register but .use)
Change your NestJS App to Fastify: (npm i --save #nestjs/platform-fastify) and then: const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter()); -> then the fastify-helmet package (and .register) should work!
More details about using fastify instead of express are available at the official documentation page: NestJS Documentation

Node modules with sub-directories: "Error parsing triggers: Cannot find module 'ibm-watson'"

I have a Firebase Cloud Function that calls IBM Watson to get a token. I'm updating it from the old username/password auth to the current IAM auth.
Here's the code from the IBM documentation:
const watson = require('ibm-watson');
const { IamAuthenticator } = require('ibm-watson/auth');
// to get an IAM Access Token
const authorization = new watson.AuthorizationV1({
authenticator: new IamAuthenticator({ apikey: 'fakekey-1234' }),
});
authorization.getToken(function (err, token) {
if (!token) {
console.log('error: ', err);
} else {
// Use your token here
}
});
When I run firebase deploy --only functions I get this error:
Error: Error parsing triggers: Cannot find module 'ibm-watson'
Require stack:
- /Users/TDK/LanguageTwo/functions/index.js
- /Users/TDK/.nvm/versions/node/v13.10.1/lib/node_modules/firebase-tools/lib/triggerParser.js
ibm-watson is installed in my /functions/node_modules directory:
I reinstalled ibm-watson, and for good measure I ran npm install in my functions directory. Plus I ran npm-check and updated all my node modules.
The specific line that causes the error is:
const watson = require('ibm-watson');
When I comment out that line the functions deploy without error. Unfortunately, the function doesn't run. :-)
This line does not cause the deploy error:
const { IamAuthenticator } = require('ibm-watson/auth');
I use IBM Watson in other Firebase Cloud Functions in the same index.js file. These lines from other functions don't cause deploy errors:
let TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
...
var LanguageTranslatorV3 = require('ibm-watson/language-translator/v3');
The problem seems to be that requiring the parent directory ibm-watson fails, but requiring the subdirectories of the parent directory works. Any suggestions?
This is as expected. If you take a look at the GitHub repo for ibm-watson - https://github.com/watson-developer-cloud/node-sdk - you will notice that there is no example requiring the top level library. This stops you from pulling in the full library, when you only need to pull in a small sub-component.

Cloud function to export Firestore backup data. Using firebase-admin or #google-cloud/firestore?

I'm currently trying to build a cloud function to export my Firestore data to my Storage Bucket.
The only example I've found on the Firebase DOCs on how to do this:
https://googleapis.dev/nodejs/firestore/latest/v1.FirestoreAdminClient.html#exportDocuments
EXAMPLE
const firestore = require('#google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient({
// optional auth parameters.
});
const formattedName = client.databasePath('[PROJECT]', '[DATABASE]');
client.exportDocuments({name: formattedName})
.then(responses => {
const response = responses[0];
// doThingsWith(response)
})
.catch(err => {
console.error(err);
});
From that example, it seems that I need to install #google-cloud/firestore as a dependency to my cloud function.
But I was wondering if I can access these methods using only the firebase-admin package.
I've thought of that because the firebase-admin has the #google-cloud/firestore as a dependency already.
> firebase-admin > package.json
"dependencies": {
"#firebase/database": "^0.4.7",
"#google-cloud/firestore": "^2.0.0", // <---------------------
"#google-cloud/storage": "^3.0.2",
"#types/node": "^8.0.53",
"dicer": "^0.3.0",
"jsonwebtoken": "8.1.0",
"node-forge": "0.7.4"
},
QUESTION:
Is it possible to get an instance of the FirestoreAdminClient and use the exportDocuments method using just the firebase-admin ?
Or do I really need to install the #google-cloud/firestore as a direct dependency and work with it directly?
The way you're accessing the admin client is correct as far as I can tell.
const client = new admin.firestore.v1.FirestoreAdminClient({});
However, you probably won't get any TypeScript/intellisense help beyond this point since the Firestore library does not actually define detailed typings for v1 RPCs. Notice how they are declared with any types: https://github.com/googleapis/nodejs-firestore/blob/425bf3d3f5ecab66fcecf5373e8dd03b73bb46ad/types/firestore.d.ts#L1354-L1364
Here is an implementation I'm using that allows you to do whatever operations you need to do, based on the template provided by firebase here https://firebase.google.com/docs/firestore/solutions/schedule-export
In my case I'm filtering out collections from firestore I don't want the scheduler to automatically backup
const { Firestore } = require('#google-cloud/firestore')
const firestore = new Firestore()
const client = new Firestore.v1.FirestoreAdminClient()
const bucket = 'gs://backups-user-data'
exports.scheduledFirestoreBackupUserData = async (event, context) => {
const databaseName = client.databasePath(
process.env.GCLOUD_PROJECT,
'(default)'
)
const collectionsToExclude = ['_welcome', 'eventIds', 'analyticsData']
const collectionsToBackup = await firestore.listCollections()
.then(collectionRefs => {
return collectionRefs
.map(ref => ref.id)
.filter(id => !collectionsToExclude.includes(id))
})
return client
.exportDocuments({
name: databaseName,
outputUriPrefix: bucket,
// Leave collectionIds empty to export all collections
// or define a list of collection IDs:
// collectionIds: ['users', 'posts']
collectionIds: [...collectionsToBackup]
})
.then(responses => {
const response = responses[0]
console.log(`Operation Name: ${response['name']}`)
return response
})
.catch(err => {
console.error(err)
})
}
firebase-admin just wraps the Cloud SDK and re-exports its symbols. You can use the wrapper, or use the Cloud SDK directly, or even a combination of the two if you want. If you want to use both, you have to declare an explicit dependency on #google-cloud/firestore in order to be able to import it directly into your code.
Here is the full explanation with code (I use it and it works very well) on how to do automated Firestore backups by mixing Cloud Scheduler, PubSub and Firebase Function https://firebase.google.com/docs/firestore/solutions/schedule-export

Can't determine Firebase Database URL when trying to read Firebase Database from within Node.js Firebase function

I am using Flutter to build an app that accesses the Firebase database. All good from the Flutter side....but I am new to Node.js and Cloud Functions. I am trying to create a Node.js function to react to a deletion event of a record on one Firebase Database node and then delete records from two other Firebase Database nodes and image files from Firestore.
I am reacting to the trigger event with a functions.database.onDelete call, no problem, but falling at the very next hurdle i.e.trying to read admin.database to get a snapshot.
I have created a dummy function that uses .onUpdate to pick up a trigger event (don't want to keep having to recreate my data as I would if I used .onDelete) and then tries to read my Firebase Database to access a different node. The trigger event is picked up fine but I don't seem to have a database reference Url to do the read...yet it is the same database. Output on the console log from a call to process.env.FIREBASE_CONFIG shows the Url is present.
The included function code also has commenting to show the various outputs I get on the console log.
I am going crazy over this.....PLEASE can anyone tell me where I am going wrong. Been searching Google, Stackoverflow, Firebase docs for the last two days :-(
const admin = require("firebase-admin"); // Import Admin SDK
const functions = require("firebase-functions"); // Import Cloud Functions
admin.initializeApp({
credential: admin.credential.cert(
require("./user-guy-firebase-adminsdk.json")
)
});
exports.testDeleteFunction = functions.database
.ref("/user-guys/{userGuyId}")
// Using .onUpdate as I don't want to keep restoring my data if I use .onDelete
.onUpdate((snapshot, context) => {
const userData = snapshot.after.val();
const userId = userData.userId;
console.log('userId: ' + userId); // Gives correct console log output: userId: wajzviEcUvPZEcMUbbkPzcw64MB2
console.log(process.env.FIREBASE_CONFIG);
// Gives correct console log output:
// {"projectId":"user-guy","databaseURL":"https://user-guy.firebaseio.com","storageBucket":"user-guy.appspot.com","cloudResourceLocation":"us-central"}
// I have tried each of the four following calls and received the console log message as indicated after each.
//
// var root = admin.database.ref(); // Console Log Message: TypeError: admin.database.ref is not a function
// var root = admin.database().ref(); // Console Log Message: Error: Can't determine Firebase Database URL.
// var root = admin.database.ref; // Fails at line 31 below with the message indicated.
// var root = admin.database().ref; // Console Log Message: Error: Can't determine Firebase Database URL.
console.log(root.toString); // Console Log Message: TypeError: Cannot read property 'toString' of undefined.
// This is intended to read a chat thread for two users but processing never gets here.
var database = root.child('chats').child('0cSLt3Sa0FS26QIvOLbin6MFsL43GUPYmmAg9UUlRLnW97jpMCAkEHE3');
database
.once("value")
.then(snapshot => {
console.log(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null; // Will do stuff here once working.
});
Error messages shown in code comments.
If you want to use the configuration in FIREBASE_CONFIG, you should initialize the Admin SDK with no parameters:
admin.initializeApp();
This will use the default service account for your project, which should have full read and write access to the database.
You need to add your database url in admin.initializeApp
admin.initializeApp({
databaseURL:"your_database_url"
});
select Realtime Database in firebase and copy your url add in settings in fire config or watch this video https://www.youtube.com/watch?v=oOm_9y3vb80
config example
const config = {
apiKey: "",
authDomain: "",
projectId: "",
databaseURL: "https://youUrl.firebaseio.com/",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};
See:
https://firebase.google.com/docs/admin/setup#initialize_without_parameters
https://firebase.google.com/docs/functions/database-events
Try initialize without parameters.
The SDK can also be initialized with no parameters. In this case, the SDK uses Google Application Default Credentials and reads options from the FIREBASE_CONFIG environment variable. If the content of the FIREBASE_CONFIG variable begins with a { it will be parsed as a JSON object. Otherwise the SDK assumes that the string is the name of a JSON file containing the options.
const admin = require("firebase-admin"); // Import Admin SDK
const functions = require("firebase-functions"); // Import Cloud Functions
admin.initializeApp();
Same issue 3 years later...
After DAYS! I found out all of the Flutter documents have code for Firestore instead of Firebase.
Basically there are two products. Firestore Database, and Real-time Database. You are calling the Real-time Database methods, but probably have a Firestore database.
Try admin.firebase().ref('/some_collection').push();
Basically everywhere you're calling .database(), replace it with .firebase() if you are using Firebase. Yes, the Flutter tutorials are mis-leading!!!

Resources