Search code examples
javascriptfirebasegoogle-cloud-firestorenestjs

How to connect to firestore database in NestJS?


I made a firestore database and I want to make CRUD operations using my NestJS backend (Node.js ofc). I added firebase to NestJS and (I assume) made the setup correctly. I use an SDK admin service account, the firestore database's rule is set to locked. Whenever I try to test the connection (either locally or in production), the firebase app is successfully initialized, but it seems the collection/document doesnt exist or for some reason I cant connect to it.

Details. part of my nestjs package.json (firebase package is there):

 "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/jwt": "^10.2.0",
    "@nestjs/platform-express": "^10.0.0",
    "bcrypt": "^5.1.1",
    "firebase-admin": "^12.1.1",
    "reflect-metadata": "^0.2.0",
    "rxjs": "^7.8.1"
  }

I created a firebase service file to initialize the app and log out if there are errors:

import { Injectable, OnModuleInit } from '@nestjs/common';
import * as admin from 'firebase-admin';
import * as serviceAccount from 'myserviceaccount.json';

@Injectable()
export class FirebaseService implements OnModuleInit {
  private firestore: FirebaseFirestore.Firestore;

  async onModuleInit() {
    //checks if firestore is already initialized, if not, do so
    if (admin.apps.length === 0) {
      admin.initializeApp({
        credential: admin.credential.cert(
          serviceAccount as admin.ServiceAccount,
        ),
      });
      console.log('Firebase initialized successfully');
    } else {
      console.log('Firebase already initialized');
    }
    this.firestore = admin.firestore();

    //check if database is connected:
    try {
      const docRef = this.firestore.doc('mycollection/mydocument');
      const snapshot = await docRef.get();
      if (snapshot.exists) {
        console.log('Document data:', snapshot.data());
      } else {
        console.log('No such document!');
      }
    } catch (error) {
      console.error('Error fetching document:', error);
    }
  }

  getFirestore() {
    return this.firestore;
  }
}

And here is the NestJS service that makes the crud operations:

import { Injectable } from '@nestjs/common';
import { FirebaseService } from '../firebase.service';

@Injectable()
export class StampService {
  private firestore: FirebaseFirestore.Firestore;

  constructor(private readonly firebaseService: FirebaseService) {
    this.firestore = this.firebaseService.getFirestore();
  }
 
  async testFirestoreConnection(): Promise<any> {
    try {
      const docRef = this.firestore.doc('mycollection/mydocument')
      const snapshot = await docRef.get();
      console.log(snapshot.data().fieldname);
      return snapshot.data();
    } catch (error) {
      console.error('Firestore test error:', error);
      throw new Error('Firestore test failed');
    }
  }
}

When I run "nest start watch", Im getting the following error:

Firebase initialized successfully
Error fetching document: Error: 5 NOT_FOUND: 
    at callErrorFromStatus (path\node_modules\@grpc\grpc-js\src\call.ts:82:17)
    at Object.onReceiveStatus (path\node_modules\@grpc\grpc-js\src\client.ts:611:51)
    at Object.onReceiveStatus (path\node_modules\@grpc\grpc-js\src\client-interceptors.ts:419:48)
    at path\node_modules\@grpc\grpc-js\src\resolving-call.ts:163:24
    at processTicksAndRejections (node:internal/process/task_queues:77:11)
for call at
    at ServiceClientImpl.makeServerStreamRequest (path\node_modules\@grpc\grpc-js\src\client.ts:594:42)
    at ServiceClientImpl.<anonymous> (path\node_modules\@grpc\grpc-js\src\make-client.ts:189:15)
    at path\node_modules\@google-cloud\firestore\build\src\v1\firestore_client.js:237:29
    at path\node_modules\google-gax\build\src\streamingCalls\streamingApiCaller.js:38:28
    at path\node_modules\google-gax\build\src\normalCalls\timeout.js:44:16
    at Object.request (path\node_modules\google-gax\build\src\streamingCalls\streaming.js:377:40)
    at makeRequest (path\node_modules\retry-request\index.js:159:28)
    at retryRequest path\node_modules\retry-request\index.js:119:5)
    at StreamProxy.setStream (path\node_modules\google-gax\build\src\streamingCalls\streaming.js:368:37)
    at StreamingApiCaller.call (path\node_modules\google-gax\build\src\streamingCalls\streamingApiCaller.js:54:16)
Caused by: Error:
    at Firestore.getAll (path\node_modules\@google-cloud\firestore\build\src\index.js:1007:23)
    at DocumentReference.get (path\node_modules\@google-cloud\firestore\build\src\reference\document-reference.js:180:32)
    at FirebaseService.onModuleInit (path\src\firebase.service.ts:26:37)
    at MapIterator.iteratee (path\node_modules\@nestjs\core\hooks\on-module-init.hook.js:22:43)
    at MapIterator.next (path\node_modules\iterare\src\map.ts:9:39)
    at IteratorWithOperators.next (path\node_modules\iterare\src\iterate.ts:19:28)
    at Function.from (<anonymous>)
    at IteratorWithOperators.toArray (path\node_modules\iterare\src\iterate.ts:227:22)
    at callOperator (path\node_modules\@nestjs\core\hooks\on-module-init.hook.js:23:10)
    at callModuleInitHook (path\node_modules\@nestjs\core\hooks\on-module-init.hook.js:43:23) {
  code: 5,
  details: '',
  metadata: Metadata {
    internalRepr: Map(1) { 'x-debug-tracking-id' => [Array] },
    options: {}
  }
}

I couldnt really try much. I checked the serviceaccount json to see if it contains the proper service account id and the proper project id.

I added the console logs and try catch blocks seen above to even get something to see what the problem is.

I made sure that my collection and document references are accurate and have no typos.

I made sure that the FirebaseModule is in my app.module as an import.

The FirebaseModule is even imported in the module the CRUD service belongs to.

Is there something wrong the way I set up firebase/firestore in NestJs, did I set up the firestore database wrong in the firebase console? Is it something else?

Any help is appreciated!


Solution

  • I think I know what was the problem. The firebase.service's onModuleInit is set to be async AND the NestJS service method that performs the CRUD operation is also async, so it might be possible that the CRUD method finishes faster than the onModuleInit in the firebase service.

    Needed to build in checks in the NestJS service method to see if the firebase app is initialized and let it perform the CRUD operation only if it is. That solved the problem.