Node-red is somehow mutating normal Object so I can't pass it into Firestore setDoc() function

I am using Firebase SDK's within Node-red (as specified in NPM docs they can be used for IoT devices with NODE.js).

  • I can use all of the CRUD methods with Firebase RealtimeDatabase.
  • With Firebase Firestore I can only use READ and DELETE functionality.
  • SET and UPDATE results in weird errors that I couldn't find answers
    anywhere on the internet.

I am importing Firebase SDK's through require() inside settiings.js and functionGlobalContext so I can access them in Node-red functions:

    functionGlobalContext: {
        firebase: require('firebase/app'),
        firebaseDatabase: require('firebase/database'),
        firebaseFirestore: require('firebase/firestore'),
        // os:require('os'),
        // jfive:require("johnny-five"),
        // j5board:require("johnny-five").Board({repl:false})
    },

First I initialize my whole Firebase project with this code (and everything initializes fine without errors):

    //Load data from Global contexta
    const firebase = global.get('firebase');
    const firebaseDatabase = global.get('firebaseDatabase');
    const firebaseFirestore = global.get('firebaseFirestore');
    
    const firebaseConfig = {
         //my Firebase credentials
    };
    
    //Set up Firebase
    const app = firebase.initializeApp(firebaseConfig);
    const database = firebaseDatabase.getDatabase();
    const firestore = firebaseFirestore.getFirestore();
    
    //Save the database reference to Global context
    global.set('app', app);
    global.set('database', database);
    global.set('firestore', firestore);

And here I am trying basic SET operation with Firestore:

    const ft = global.get('firebaseFirestore');
    const firestore = global.get('firestore');
    
    const frankDocRef = ft.doc(firestore, "users", "frank");
    await ft.setDoc(frankDocRef, {
        name: "Frank",
        age: 12
    });

Unfortunately even though this code is ctrl+c ctrl+v from Firestore docs I get this error:

"FirebaseError: [code=invalid-argument]: Function setDoc() called with invalid data. Data must be an object, but it was: a custom Object object (found in document users/frank)"
  • When I use the same code inside a web app everything works fine.
  • There has to be something going on under the hood with Node-red
  • I tried creating the object using various methods and all of them resulted in the same error.

Does anybody have any idea what could be going wrong here?

It seems that Node-red is somehow passing to the setDoc function mutated object. Or does anybody know how to create object and not Object object? I always though that those two things are the same.

Have you tried using node-red-contrib-firebase-data.
( It was last updated about a year ago )

When you try the SET code, is this in a seperate Function node ?
(screenshot to better understand the flow)

avoid setting to global Context for now and try to get a simply read / write going

Setup functionExternalModules in your settings file (read how to do that here)
and try this example

const { getFirestore, collection, getDocs, addDoc } = firestore

//change to your config
const firebaseConfig = {
  apiKey: "AIzaSyDmXgb_58lO7aK_ujN37pGlNxzWGEU0YpI",
  authDomain: "fb9-sandbox.firebaseapp.com",
  projectId: "fb9-sandbox",
  storageBucket: "fb9-sandbox.appspot.com",
  messagingSenderId: "867529587246",
  appId: "1:867529587246:web:dc754ab7840c737f47bdbf"
}

// init firebase
firebaseApp.initializeApp(firebaseConfig)

// init services
const db = getFirestore()

// collection ref
const colRef = collection(db, 'users')   // change to your collection

// get collection data
getDocs(colRef)
  .then(snapshot => {
    // console.log(snapshot.docs)
    let data = []
    snapshot.docs.forEach(doc => {
      data.push({ ...doc.data(), id: doc.id })
    })
    node.warn({ data })
  })
  .catch(err => {
    node.warn({ err: err.message })
  })


// adding docs
addDoc(colRef, {
  name: "Frank",
  age: 12
})
  .then(() => {
    node.warn("User added")
  })




( example based on theNetNinja tutorial )

1 Like

I found some time and tested it also and i was getting the same error with you when trying to write to firestore.

i read a bunch of articles but couldnt find a solution

i tried the @google-cloud/firestore library though and that seems to work

Documentation of how to set that up here
Dont ask how i set it up because honestly i dont remember :wink:
(that google Cloud console dashboard is a labyrinth)

Test code:

const { Firestore } = googleCloudFirestore

// Initialize Firebase
const db = new Firestore({
  projectId: 'nodered-firestore',
  keyFilename: 'firebaseKey.json',
});


// Delete document
//  const res = await db.collection('users').doc('aVikxnsoV8RD9S6jMhNs').delete();
// node.warn({ res })

// delete all docs
// db.collection('users').get().then(querySnapshot => {
//     querySnapshot.docs.forEach(snapshot => {
//         snapshot.ref.delete();
//     })
// })


// add a doc
const docRef = db.collection('users').doc();
await docRef.set({
  name: 'John',
  age: 12
});

// read all docs
const snapshot = await db.collection('users').get();
let data = []
snapshot.forEach((doc) => {
  data.push({ id: doc.id, ...doc.data() })
});

node.warn(data);

1 Like

Wow! Thank you for your time invested in this.

  • I had no idea there is a way to communicate to Firestore with any other libraries then the native JS SDK's.

  • I will definitely take a look at @google-cloud/firestore!

  • But yes - as you said the configuration of Google Cloud can be somehow messy. That's why I was looking for solution in Firebase (it is really easy to setup and work with it).

If I won't find a solution on how to use the native SDK's I'll have to use the @google-cloud/firestore. But I will keep looking on how to make the native SDK's working.

By any chance you didn't come up with a working example?

The suggested library is for Firebase Realtime database not for Firestore :confused:

hmm possibly .. sorry i didnt install it.

I did install this node and it works!

This library uses under the hood Admin SDK's. Which works fine (as you said).

But if my IoT device is compromised my whole Firebase is in danger and can be manipulated or deleted as the attacker pleases :confused:

Thats a totally different topic which i dont know much about but ..
why would it compromised ? .. will you have Node-red open and accessible from the internet ?

  1. Read this article on securing Node-red here
  2. Use a strong password
  3. Firewall all unnecessary connections and ports
1 Like

Well I won't. But I am sure there is always a way inside some system's. The Node-red will run on Raspberry PI. And I think with some time and brute force the attacker could get inside.

So what exactly is the question you are asking?

1 Like

How to use the native Firestore JavaScript SDK's inside Node-red (not Admin SDK's or @google-cloud/firestore library).