Search code examples
typescriptgoogle-cloud-functionsgeofirex

How to use geofirex inside google cloud functions


I have a use case where I need to use geofirex library inside Google Cloud Functions. The init function of the library expects FirebaseApp instance. Is it possible to initialise it inside cloud functions in order to query firestore database using firebase-admin?

What i tried..

functions > src > index.ts

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
admin.firestore().settings({timestampsInSnapshots: true});


// Init GeoFireX
import * as firebase from 'firebase/app';
import * as geofirex from 'geofirex';
const geoFire = geofirex.init(firebase);

exports.geofenceUserNotifications = functions.https.onRequest((req, res) => {
  if(!req || !req.body || !req.body.length || !req.body[0].latitude || !req.body[0].longitude || !req.body[0].uid || !req.body[0].userFcmToken){
    // end function with 401 status
    res.status(401).send('Authentication required.');
  }

  const uid = req.body[0].uid;
  const userFcmToken = req.body[0].userFcmToken;
  const latitude = req.body[0].latitude;
  const longitude = req.body[0].longitude;
  const field = 'position';

  geoFire.collection('users').setPoint(uid, field, latitude, longitude).then(result => {
    res.end('User geoPoint updated!');
  }).catch( err => {
      console.log(err);
      res.end('error when update user geo point!');
  });

});

functions > package.json

{
  "name": "functions",
  "scripts": {
    "lint": "tslint --project tsconfig.json",
    "build": "tsc",
    "serve": "npm run build && firebase serve --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log"
  },
  "main": "lib/index.js",
  "dependencies": {
    "firebase-admin": "~6.0.0",
    "firebase-functions": "^2.1.0",
    "moment": "^2.23.0",
    "firebase": "^5.10.1",
    "geofirex": "0.0.6",
    "rxjs": "^6.3.3",
    "rxjs-compat": "^6.5.1"
  },
  "devDependencies": {
    "tslint": "~5.8.0",
    "typescript": "~2.8.3"
  },
  "private": true
}

No error when deploy but when call geofenceUserNotifications function i get this error:

TypeError: app.firestore is not a function
    at new GeoFireCollectionRef (/user_code/node_modules/geofirex/dist/index.cjs.js:1465:24)
    at GeoFireClient.collection (/user_code/node_modules/geofirex/dist/index.cjs.js:1639:16)
    at exports.geofenceUserNotifications.functions.https.onRequest (/user_code/lib/index.js:2197:13)
    at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:57:9)
    at /var/tmp/worker/worker.js:783:7
    at /var/tmp/worker/worker.js:766:11
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)

Thanks in advance.


Solution

  • the problem solved as following:

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp();
    admin.firestore().settings({timestampsInSnapshots: true});
    
    // Init GeoFireX
    const geofirex = require('geofirex');// <-- Use require Instead of import geofirex
    import { get } from 'geofirex';
    const geoFire = geofirex.init(admin); // <-- admin not admin.app()
    

    Then

    exports.geofenceUserNotifications = functions.https.onRequest(async (req, res) => {
    
      //setPoint (id, field, lat, lng)
      geoFire.collection('users').setPoint(uid, field, latitude, longitude).then(result => {
        res.end('User geo point is updated!');
      }).catch( err => {
          res.end('error when update user geo point');
          return;
      });
    });
    

    And geoFireX works fine.

    but now I faced other problem with geoFireX query. I tried get nearby items but geoFireX returns Observable<object[]> and it's not work within cloud functions.

    I tried use get() by geoFireX:

    const query = geoFire.collection('places', queryRef).within(center, radius, field);
    const nearbyPlaces = await get(query); // await query.pipe(operators.first()).toPromise();
    

    but unfortunately it does not work. and I get the following error:

    Error: Argument "onNext" is not a valid function. at Validator.(anonymous function).values [as isFunction] (/srv/node_modules/@google-cloud/firestore/build/src/validate.js:99:27) at Query.onSnapshot (/srv/node_modules/@google-cloud/firestore/build/src/reference.js:1524:25) at Observable._subscribe (/srv/node_modules/geofirex/dist/index.cjs.js:1597:33) at Observable._trySubscribe (/srv/node_modules/rxjs/internal/Observable.js:44:25) at Observable.subscribe (/srv/node_modules/rxjs/internal/Observable.js:30:22) at MapOperator.call (/srv/node_modules/rxjs/internal/operators/map.js:32:23) at Observable.subscribe (/srv/node_modules/rxjs/internal/Observable.js:25:31) at Object.subscribeToResult (/srv/node_modules/rxjs/internal/util/subscribeToResult.js:12:23) at CombineLatestSubscriber._complete (/srv/node_modules/rxjs/internal/observable/combineLatest.js:76:46) at CombineLatestSubscriber.Subscriber.complete (/srv/node_modules/rxjs/internal/Subscriber.js:78:18)