glebbash / nestjs-fireorm

Nest.js Fireorm module
https://glebbash.github.io/nestjs-fireorm
MIT License
15 stars 10 forks source link

Nest-fireorm integration #12

Closed hchaucha closed 1 month ago

hchaucha commented 5 months ago

Overview

Hi guys, I'm having some problems integrating Nest-fireorm and/or Fireorm. I'm not sure what I'm missing.

I followed these steps:

How to reproduce issue

Start by creating a new dumb project with all required dependencies :

nest new myAwesomeApp
npm install --save nestjs-fireorm fireorm
npm install --save firebase-admin
nest generate resource users

Update main.ts file to initialize Firestore :

Following Fireorm documentation (see : Fireorm usage)

# *********************
# **     main.ts     **
# *********************

import * as admin from 'firebase-admin';
import * as fireorm from 'fireorm';
import * as serviceAccount from './dev.service-account.json';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  initializeFirestore();
  await app.listen(3000);
}

function initializeFirestore() {
  admin.initializeApp({
    credential: admin.credential.cert(<admin.ServiceAccount>serviceAccount),
  });

  const firestore = admin.firestore();
  firestore.settings({ timestampsInSnapshots: true });
  fireorm.initialize(firestore);
}

bootstrap();

At this point I'm able to initialize my Firestore instance and use it as follow :

const snap = await firestore.collection('users').get();
console.log('size :', snap.size);

Import Fireorm Module to app.module.ts :

Following nest-fireorm documentation (see : nest-fireorm readme)

# ***************************
# **     app.module.ts     **
# ***************************

import { Module } from '@nestjs/common';
import { FireormModule } from 'nestjs-fireorm';

@Module({
  imports: [
    FireormModule.forRoot({
      firestoreSettings: { projectId: 'myAwesomeAppDev' },
      fireormSettings: { validateModels: true },
    }),
    UsersModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Define the User entity / collection / repository :

_Following Fireorm documentation (see : Fireorm Core-concepts)_

# *****************************
# **     users.entity.ts     **
# *****************************

import { Collection } from 'fireorm';

@Collection()
export class User {
  id: string;
  firstname: string;
  lastname: string;
  email: string;
}

// const usersRepository = getRepository(User);

Here, I can't use getRepository() function as mentioned in Fireorm doc; thrown the following error :

Error: Firestore must be initialized first

So I tried to skip this part since it's not mentionned on Nest-fireorm documentation.

Update users.module.ts to Inject User repository :

Following Nest-fireorm documentation (see : nest-fireorm readme)

# *****************************
# **     users.module.ts     **
# *****************************

import { Module } from '@nestjs/common';
import { FireormModule } from 'nestjs-fireorm';

import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { User } from './entities/user.entity';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
  imports: [FireormModule.forFeature([User])],
})
export class UsersModule {}

In documentation, .forFeature() function is called in app.module.ts which throw the following error :

Nest can't resolve dependencies of the UsersController (?, UsersService). Please make sure that the argument "UserRepository" at index [0] is available in the UsersModule context

Finally, trying to consume User repository from users.controller.ts / users.service.ts :

# *****************************
# **     users.controller.ts     **
# *****************************

import { Controller, Get, Param } from '@nestjs/common';
import { InjectRepository } from 'nestjs-fireorm';
import { BaseFirestoreRepository } from 'fireorm';

import { UsersService } from './users.service';
import { User } from './entities/user.entity';

@Controller('users')
export class UsersController {
  constructor(
    @InjectRepository(User) private users: BaseFirestoreRepository<User>,
    private readonly usersService: UsersService,
  ) {}

  @Get()
  async findAll(): Promise<Array<User>> {
    try {
      const users = await this.users.find();
      return users;
    } catch (err) {
      console.error(err);
      return err;
    }
  }

  @Get(':id')
  async findOne(@Param('id') id: string) {
    const user = await this.users.findById(id);
    return user;
  }
}

In the best case, I have no crash but can't fetch my collection (results in null or empty array). Thank you for your help and your time !

glebbash commented 5 months ago

Hi @hchaucha 👋 Can you try downloading example project from here and modify it to suit your use case.

hchaucha commented 5 months ago

Hello, Thank you for the quick response. I created a dumb project based on provided example : https://github.com/hchaucha/nest-fireorm-test.

Obviously, I did modify the service account file and update all sensitive data with anonymized one.

The only difference with provided example is the Firestore initialization in the main.ts (mentionned on Fireorm doc) : I don't understand how it could works without it ?!

async function initializeFirestore() {
  admin.initializeApp({
    credential: admin.credential.cert(<admin.ServiceAccount>serviceAccount),
  });

  const firestore = admin.firestore();
  firestore.settings({ timestampsInSnapshots: true });
  fireorm.initialize(firestore);
}
glebbash commented 5 months ago

So fireorm.initialize is called inside FireormService here.

And then FireormService.getRepository is called for each entity that you register with InjectRepository.

So you don't have to call fireorm.initialize yourself again because that would probably reset some state in fireorm.

hchaucha commented 5 months ago

Ok, understood !

But still, can't instantiate my database correctly : controller returns an empty array when .find() function is fire.

Is the admin.initializeApp() function required ? Is it possible to connect the database with project_id only ?

glebbash commented 5 months ago

You can provide you own firestore instance like this (a quick sketch, might require modifications):

FireormModule.forRootAsync({
  useFactory: () => {
    admin.initializeApp({
      credential: admin.credential.cert(serviceAccount as admin.ServiceAccount),
    });

    const firestore = admin.firestore();
    firestore.settings({ timestampsInSnapshots: true });

    return { firestore };
  },
),
glebbash commented 1 month ago

Closing this as stale