Using Firestore and Typescript for Efficient Dynamic Where Conditions

My goal is to incorporate the Repository Pattern using Firestore Firebase and TypeScript.

Here is the code snippet:

import { firestore } from "firebase-admin";
import { ISearchCriteria } from './ISearchCriteria'

export class DBContext {
    static current: DBContext = new DBContext();
    private db: firestore.Firestore;

    private constructor() {
        this.db = firestore();
    }

    collection(collectionName: string): firestore.CollectionReference<firestore.DocumentData> {
        return this.db.collection(collectionName);
    }

    public async where<T extends object>(collectionName: string, searchCriteria: ISearchCriteria[]): Promise<T[]> {
        var snapShot = this.collection(collectionName);
        for (var i = 0; i < searchCriteria.length; i++) {
            snapShot = snapShot.where(searchCriteria[i].fieldName, searchCriteria[i].filterOp, searchCriteria[i].value);
        }
        let res = await snapShot.get();
        return res.docs.map<T>(doc => ({ id: doc.id, ...doc.data() }) as T);
    }


    async get<T extends object>(collectionName: string): Promise<T[]> {
        let res = await this.collection(collectionName).get();
        return res.docs.map<T>(doc => ({ id: doc.id, ...doc.data() } as T));
    }

    async create<T extends object>(collectionName: string, item: T): Promise<T> {
        return (await this.collection(collectionName).add(item)) as T;
    }

    async update<T extends object>(collectionName: string, id: string, item: T): Promise<firestore.WriteResult> {
        var docRef = this.collection(collectionName).doc(id);
        return await docRef.update(item);
    }

    async delete<T extends object>(collectionName: string, id: string): Promise<firestore.WriteResult> {
        return await this.collection(collectionName).doc(id).delete();
    }
}

I tried following an example provided in this link.

However, I encountered the following error message:

dbcontext.ts:23:13 - error TS2740: Type 'Query<DocumentData>' is missing the following properties from type 'CollectionReference<DocumentData>': id, parent, path, listDocuments, and 2 more.
The error occurs at this specific line:

snapShot = snapShot.where(searchCriteria[i].fieldName, searchCriteria[i].filterOp, searchCriteria[i].value);

This is the structure of ISearchCriteria:

import { firestore } from "firebase-admin";
export interface ISearchCriteria {
    fieldName: string | firestore.FieldPath,
    filterOp: FirebaseFirestore.WhereFilterOp,
    value: any
}

Answer №1

The issue lies in the fact that your collection() function is returning a CollectionReference, while the where() method returns a Query. TypeScript is flagging this as an error because you are attempting to assign a Query object to a variable of type CollectionReference.

If you refer to the official API documentation, you will notice that a CollectionReference actually extends from Query (essentially representing all documents within a collection). When you use the where() method, it creates a new Query instance that filters the documents based on specific criteria.

To resolve this issue, adjust your collection() function to return a Query instead:

collection(collectionName: string): firestore.Query<firestore.DocumentData> {
    return this.db.collection(collectionName);
}

Additionally, consider renaming your variable from snapShot to query since it more accurately reflects its purpose.

Answer №2

function fetchDocuments(collectionName, queries) {
  let ref = db.collection(collectionName);
  if (queries && queries.length) {
    for (let query of queries) {
      ref = ref.where(
        query.field,
        query.condition,
        query.value
      );
    }
  }
  return ref.get();
}

Using the specified collection type from firebase.firestore.Query was a game-changer.

let ref = db.collection(collectionName);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Modifying the NestJS Decorator string parameter upon library import: A step-by-step guide

I am working with a NestJs monorepo that contains several Apps (microservices) and Libs. One common Service class is used across all apps, so I decided to extract it into a separate lib. Initially, I thought this was a good idea. However, I soon realized ...

Reactjs may have an undefined value for Object

I have already searched for the solution to this question on stackoverflow, but I am still confused. I tried using the same answer they provided but I am still getting an error. Can someone please help me resolve this issue? Thank you. const typeValue = [ ...

Disabling ion-select in Ionic 2 with Typescript

To disable an ion-select element in Angular, you can use the disabled attribute like this: <ion-item> <ion-label stacked>Property Type</ion-label> <ion-select [(ngModel)]="propType" (ionChange)="ionChanger()" di ...

How to Retrieve Input Field Value Using Cypress Custom Command

Is there a way to retrieve the value of an input[text] element within a custom command? Cypress.Commands.add('extendValue', { prevSubject: 'element' }, (subject: JQuery<HTMLElement>, extension: string): any => { const r ...

Unit testing the error function within the subscribe method in Angular

I've been working on a unit test for the subscribe call, but I'm struggling to cover the error handling aspect of the subscribe method. The handleError function deals with statusCode=403 errors and other status codes. Any assistance would be grea ...

The VueFire object is not defined

Here's my code snippet: const firebase = { items: { source: db.ref('items'), asObject: true, readyCallback: function() { console.log('items retrieved!'); } } } new Vue({ el: '#app', firebas ...

Is it possible to use Angular signals instead of rxJS operators to handle API calls and responses effectively?

Is it feasible to substitute pipe, map, and observable from rxjs operators with Angular signals while efficiently managing API calls and their responses as needed? I attempted to manage API call responses using signals but did not receive quick response t ...

Leveraging thousands of on() event listeners in Firebase demonstrates exceptional design

We are looking to perform operations on our Firebase database and modify data based on user input from their mobile devices by changing a flag. Currently, we are using the `on()` method to listen to specific flags within each user's node. This listen ...

What could be the reason it's not functioning as expected? Maybe something to do with T extending a Record with symbols mapped

type Check<S extends Record<unique, unknown>> = S; type Output = Check<{ b: number; }>; By defining S extends Record<unique, unknown>, the Check function only accepts objects with unique keys. So why does Check<{b:number}> ...

Having trouble setting the default value of a select element with 'selected' in Angular's bootstrap?

Click here I've been facing some difficulties in making the 'selected' tag work to pre-select my default select value. It seems like it might be related to the unique pipe I'm using and how Angular handles it. I have experimented with ...

Tips for submitting a request following a change in the variable

I am in the process of developing a React application and I have implemented Auth0 for authentication. My goal is to initiate an HTTP request upon page refresh, but only if the variable isLoading is false. This way, I can access the user object once the ...

Is there a way to find the recursive key types in TypeScript?

Is there a method to ensure that code like this can compile while maintaining type safety? type ComplexObject = { primitive1: boolean; complex: { primitive2: string; primitive3: boolean; } }; interface MyReference { myKey: keyof ComplexObj ...

IE11 is encountering an error with an "Expected identifier" message in the vendor.js file related to Webpack's variable

I encountered an issue with an Expected identifier ERROR code: SCRIPT1010 that is pointing to vendor.js in the WEBPACK VAR INJECTION section. The debugger is highlighting the line with: const { keccak256, keccak256s } = __webpack_require__(/*! ./hash */ ...

Error Passing Data to Child Component in Typescript on Next.JS 14 Compilation

Although there are many similar questions, I am struggling to make my code work properly. I recently started learning typescript and am having trouble passing data to the child component. While the code runs fine in development (no errors or warnings), i ...

Navigating through multiple pages using an Observable in Angular

After countless attempts, I still haven't been able to figure it out. Any assistance would be greatly appreciated; I recently came across Angular and RxJs. The issue I'm facing involves a service that fetches resources from various URLs of the s ...

Issue encountered while attempting to remove a post from my Next.js application utilizing Prisma and Zod

Currently, I'm immersed in a Next.js project where the main goal is to eliminate a post by its unique id. To carry out this task efficiently, I make use of Prisma as my ORM and Zod for data validation. The crux of the operation involves the client-sid ...

Experimenting with PIXI.js and Jest within a React Single Page Application

I am currently working on a react application that utilizes PIXI.js and @inlet/react-pixi for animations. During testing with Jest, I encountered the following errors: Error: Uncaught [TypeError: Cannot read properties of null (reading 'stage' ...

What is the process for transferring a function to reducers in Redux Toolkit?

In one of my files called Main.tsx, I have a function that sends a request and retrieves data: async function fetchProducts(productsPage = 1, id?: number) { const itemsPerPage = 5 let url: string if (id) { url = `https://reqres.in/api/ ...

Authentication for file uploads in Angular 2 using Dropzone and passportjs

I am currently working on implementing authentication for an admin user using Express, Passport, and MySQL in a specific page. The authentication process works fine, but I am facing an issue with verifying whether the user is logged in while uploading file ...

Using Typescript, you can specify an array of type <T> within an object

Having a background in JS, I am currently exploring TS. My goal is to create an object with a single field, which is an array of strings, while ensuring strong typing. let container = { items: ['Item 1'] } container.items[0] = 3; // This is i ...