skip to Main Content

I’m creating a NestJS app with Firebase cloud functions. I have to use both onRequest and onCreate (Firestore Events) from Firebase in the NestJS application. It’s quite straightforward how to address the onRequest events. However, I’m not sure if I’m doing it right when having to do both at the same time. In order for me to pass the onCreate event changes and context to the service layer, I need to get access to the AppService class. To do that I need access to the app instance that has been created. However, I feel like I’m creating two instances of the app (refer to the code). I would like to know if my current my implementation is best to practice or if there is any way it can be improved. Please note I’m a frontend developer so this work is outside my comfort zone. I would like to know the best way to do this, especially if I have to work with more events such as onUpate, onDelete etc.

import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import * as express from 'express';
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { Express } from 'express-serve-static-core';
import { AppService } from './app.service';

const server = express();

export const createNestServer = async (expressInstance: Express) => {
  //FIRST APP INSTANCE
  const app = await NestFactory.create(
    AppModule,
    new ExpressAdapter(expressInstance),
  );
  admin.initializeApp();
  return app.init();
};

createNestServer(server)
  .then((v) => console.log('Nest Ready'))
  .catch((err) => console.error('Nest broken', err));

//exporting all onRequest events
export const api = functions.https.onRequest(server);
//exporting the  onUserCreate event
exports.onUserCreate = functions.firestore
  .document('users/{docId}')
  .onWrite(async (change, context) => {
    console.log('Changes are tracked');
    //SECOND APP INSTANCE
    const app = await NestFactory.create(AppModule);
    return app.get(AppService).onCreate(change, context);
  });

2

Answers


  1. Chosen as BEST ANSWER

    The following approach has allowed me to solve the multiple instatiation issue of the app instance. Hopefully this is a good and acceptable way to do this. Feedback is more than welcome.

    const server = express();
    
    export const createNestServer = async (expressInstance: Express) => {
      //FIRST APP INSTANCE
      const app = await NestFactory.create(
        AppModule,
        new ExpressAdapter(expressInstance),
      );
    
      admin.initializeApp();
      return app.init();
    };
    
    const main = createNestServer(server);
    
    export const api = functions.https.onRequest(server);
    
    exports.onUserWrite = functions.firestore
      .document('users/{docId}')
      .onWrite((change, context) =>
        main.then((app) => {
          return app.get(AppService).onCreate(change, context);
        }),
      );
    

  2. I assume you have something like a UsersController (and others) that handles retrieving, creating, updating the users as part of the NestJS application. Just pass the Express instance (wrapped by the NestJS adapter) to the Cloud Function HTTPS handler (onRequest). NestJS will take care of setting up the routes.

    export const api = functions.https.onRequest(server)
    
    @Controller('users')
    export class UsersController {
      constructor(private readonly usersService: UserService) {}
    
      @Get(':userId')
      async getUser(@Param('userId') id: String) {
        // retrieve user
      }
    
      @Post()
      async create(@Body() createUserDto: CreateUserDto) {
        // create a new user
      }
    
      ...
    }
    
    @Module({
      controllers: [UsersController],
      providers: [AppService, UsersService],
      exports: [AppService, UsersService]
    })
    export class AppModule {}
    

    The AppService is a provider registered in the AppModule. You could extract it from the INestApplication instance.

    const server = express();
    let appService: AppService;
    
    export const createNestServer = async (expressInstance: Express) => {
      const app = await NestFactory.create(
        AppModule,
        new ExpressAdapter(expressInstance),
      );
      admin.initializeApp();
      appService = app.get(AppService);
      return app.init();
    };
    

    Then you could use it within the onWrite firestore trigger.

    Seems like you are trying to do 2 differents tasks with the same NestJS app. Just let the NestJS (or ExpressJS) app handle the HTTPS requests. And for the onCreate, onWrite … triggers implement a different solution. Perhaps don’t rely on NestJS to handle these types of triggers.

    NestJS personally feels like overkill, you could just use ExpressJS. You wouldn’t have to register the AppService as a provider and jump through hoops.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search