Liam
Liam

Reputation: 675

How to perform a firestore query inside a Firebase function?

I have a firestore query inside my firebase function that is used to retrieve a document if it has a specific value inside it (eg: if the name field is equal to "name"). I have written the code and declared the function as a functions.https.onCall so I can call it within my app. Calling the function works however it doesn't do anything when it has started. Here is the query that I have written that is causing the problem:

let query = admin.firestore().collection('...').where('...', '==', value).orderBy('...').limit(1);
        query.get().then(snapshot => {
            let ... = snapshot[0];

Here is my function declaration:

exports.functionName = functions.https.onCall((data, context) => {

What the function should do is log what was passed into it from the calling code, perform the query (currently testing with valid data), and continue with the rest of the function. Right now it doesn't do anything but when I remove the where function it works but cannot get the specific document I am looking for. Thank you for any insights into my problem.

Upvotes: 10

Views: 12233

Answers (3)

xinthose
xinthose

Reputation: 3820

I figured it out. FirebaseUserIntf is my interface of what the collection looks like.

import * as functions from "firebase-functions";
import {initializeApp, App} from "firebase-admin/app";
import {getFirestore, Query, QuerySnapshot, QueryDocumentSnapshot, DocumentData, CollectionReference, Firestore} from "firebase-admin/firestore";
import {Request, Response} from "express";

import {FirebaseUserIntf} from "./interfaces/FirebaseUserIntf";

export const AutopayService = functions.https.onRequest(async (req: Request, res: Response) => {
    try {
        const firebaseApp: App = initializeApp();
        const firestore: Firestore = getFirestore(firebaseApp);
        const userColl: CollectionReference<DocumentData> = firestore.collection("User");

        // create query
        const q1: Query<DocumentData> = userColl.where("AutopayUsed", "==", true).where("DeleteInd", "==", false);

        // get data
        const querySnapshot: QuerySnapshot<DocumentData> = await q1.get();

        // see if any users need their balance replenished
        if (querySnapshot.size) {
            // get users
            const firebaseUsers: Array<FirebaseUserIntf> = [];
            querySnapshot.forEach((doc: QueryDocumentSnapshot<DocumentData>) => {
                const firebaseUser = doc.data() as FirebaseUserIntf;
                firebaseUsers.push(firebaseUser);
            });

            // iterate over users
            for (const firebaseUser of firebaseUsers) {
                // check balance, insert payment, etc.
            }

            // return OK
            res.sendStatus(200);
        } else {
            const msg = "there are no users who need balance replenishment";
            res.status(200).send(msg);
        }
    } catch (error) {
        res.status(500).send(error);
    }
});

Upvotes: 2

moof2k
moof2k

Reputation: 1897

I had a similar need and found this question when searching for an example. This worked for me:

const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();

exports.doQuery = functions.https.onRequest(async (req, res) => {

  var query = await admin.firestore().collection('COLLECTION_NAME')
    .where('FIELD_NAME', '==', 'FIELD_VALUE')
    .get().then(result => {
        result.forEach((doc) => {
            console.log(doc.id, doc.data());
        });
    });
}

A few things tripped me up when I was trying to piece this together on my own:

  • In a cloud function you get the firestore interface from the admin interface. Call admin.firestore() to get it.
  • Any number of errors or compilation errors will crash the emulator, and when the emulator stops your test data goes away. So if you're trying this with the emulator remember you need to re-create your test data whenever you restart it.

Upvotes: 10

Andrei Cusnir
Andrei Cusnir

Reputation: 2805

Based on the code example you provided, I assume that you are using Nodejs Cloud Function. What you need to do is to list all the documents in the collection and get the data field you want from each document.

Your package.json file should look something like this:

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "firebase-admin": "^6.5.1"
  }
}

Then you need to establish connection with Firebase:

    const admin = require('firebase-admin');

    admin.initializeApp({
      credential: admin.credential.applicationDefault()
    });

    var db = admin.firestore();

And run this code to list all the documents in a collection:

    //From collection -> COLLECTION_NAME
    db.collection('COLLECTION_NAME').get()
    .then((snapshot) => {
      snapshot.forEach((doc) => { 
        //Checking each document in that collection
        //Log the document ID and the ClientID field from each document in the collection
        console.log(doc.id, '=>', doc.data().clientID);
      });
    })
    .catch((err) => {
      console.log('Error getting documents', err);
    });

    //Return a message to the main function to print a message on browser notifying about the successful operation
    return 'Finished checking';

Instead of COLLECTION_NAME use the name for your collection. And instead of clientID filed in doc.data().clientID, use the field you are looking for. Perform the if statement there.

For example:

if (doc.data().name == 'name'){
    //Do something 
}

Upvotes: 3

Related Questions