Home > Mobile >  firebase function : update document with synchronous rest call
firebase function : update document with synchronous rest call

Time:09-18

I have this scheduled function that :

  1. read 1 document from collection

    data: [ 
     {
      'field1' : 123,
      'field2' : 456
     },
     {
      'field1' : 123,
      'field2' : 456
     }
     ...
    ]
    
  2. loop on all data array

  3. read new value from rest api call

  4. update the value of data array in firestore

NOTE from firebase function console log I have the log 'field updated ...' after the log 'firebase document updated ...', I think because the request is not sync

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const db = admin.firestore();
const fetch = require('node-fetch');
var request = require("request");
const { Headers } = fetch;
exports.onStatisticUpdated = functions.region('europe-west1')
  .runWith({
    timeoutSeconds: 300,
  })
  .pubsub.schedule('every 60 minutes')
  .onRun(async context => {
    const doc = await admin
      .firestore()
      .collection('collection')
      .doc('1234')
      .get();
    if (doc.exists) {
      for (let i = 0; i < doc.data().data.length; i  ) {
        let myFirebaseData = doc.data().data[i];
        var options = {
          method: 'GET',
          url: 'https://xyz....',
          qs: { abcd: 'value' },
          headers: {
            'x-xxxx-host': 'v1....',
            'x-xxxx-key': 'xxxxxxxxxxxx'
          }
        }
        request(options, function (error, response, body) {
          if (error) throw new Error(error);
          var json = JSON.parse(body);
          if (json['response'].length > 0) {
            console.log('field updated ...');
            myFirebaseData.field1 = json['response'][0].value1
            myFirebaseData.field2 = json['response'][0].value2
          };
        });
      }
      // myFirebaseData is not updated at this time with new value filled by rest api !!!
     console.log(' firebase document updated ...');
      await admin
        .firestore()
        .collection('collection')
        .doc('1234')
        .update({
          data: doc.data(),
        });
    }
  });

question : how i can store the final document with new values filled by the rest api ?

CodePudding user response:

In Cloud Functions you need to manage asynchronous method calls via Promises (more details here in the doc). request supports callback interfaces natively but does not return a Promise.

You should use another library, like axios. In addition, since you want to execute a variable number of asynchronous Rest API calls in parallel, you need to use Promise.all().

Now, what is not clear to me in your code is how do you build the object used to update the 1234 document. I your current code, in the for (let i = 0; i < doc.data().data.length; i ) {}) loop you are actually overwriting the field1 and field2 properties of the myFirebaseData again and again...

Therefore you will find below the code structure/pattern that I think is correct and if it is not the case, just add a comment to this answer and we can fine tune the answer according to the extra details you will share.

exports.onStatisticUpdated = functions.region('europe-west1')
    .runWith({
        timeoutSeconds: 300,
    })
    .pubsub.schedule('every 60 minutes')
    .onRun(async context => {
        const doc = await admin
            .firestore()
            .collection('collection')
            .doc('1234')
            .get();

        if (doc.exists) {
            const promises = [];
            for (let i = 0; i < doc.data().data.length; i  ) {
                let myFirebaseData = doc.data().data[i];
                var options = {
                    method: 'get',
                    url: 'https://xyz....',
                    params: { abcd: 'value' },
                    headers: {
                        'x-xxxx-host': 'v1....',
                        'x-xxxx-key': 'xxxxxxxxxxxx'
                    }
                }
                promises.push(axios(options))
            }
            apiResponsesArray = await Promise.all(promises);

            const updateObject = {};
            apiResponsesArray.forEach((resp, index) => {
                // THIS ENTIRE BLOCK NEEDS TO BE ADAPTED!!
                // I'M JUST MAKING ASSUMPTIONS...
                const responseData = resp.data;
                updateObject["field1"   index] = responseData.value1;
                updateObject["field2"   index] = responseData.value2;

            })

            console.log(updateObject)

            console.log(' firebase document updated ...');
            await admin
                .firestore()
                .collection('collection')
                .doc('1234')
                .update({
                    data: updateObject
                });
                
            return null;  // IMPORTANT!!! see the link to the doc above
        }
    });
  • Related