Search code examples
javascriptnext.jsnestjstypeormfastify

Nest + Fastify + fasity-next + TypeOrm cannot find next() function


I am trying to initialize Nest with Fastify and Next using the fastify-next plugin, everything was okay until I integrated TypeOrm (MongoDB). When Nest loads the AppModule it throws an error that says the .next() function is not found in the fastify instance after the fastify-next plugin is initialized.

Code

// main.ts
async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );

  await app.init();

  const configService = app.get<ConfigService<IConfigTypes>>(ConfigService);
  const appConfig = configService.get<IAppConfig>('app');

  const fastify = await app.register(fastifyNext, {
    dev: appConfig.isDev,
    dir: appConfig.clientPath,
  });

  fastify.after(() => {
    appConfig.staticRoutes.forEach((page) => fastify.next(page));
  });

  await app.listen(appConfig.port);
  Logger.log(`🚀 Server is running on port ${appConfig.port}`, 'Bootstrap');
}
bootstrap();
// app.module.ts
@Module({
  imports: [
    ConfigModule.forRoot({
      validate,
      isGlobal: true,
      cache: true,
      load: [appConfig, shopifyConfig, databaseConfig],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService<IConfigTypes>) => {
        const dbConfig = configService.get<IDatabaseConfig>('database');
        const { type, host, base, user, pass } = dbConfig;

        let url = 'mongodb+srv://';
        url += `${user}:${pass}@${host}/${base}`;
        url += '?retryWrites=true&w=majority';

        return {
          type,
          url,
          entities: [join(__dirname, '**/**.entity{.ts,.js}')],
          synchronize: true,
          useNewUrlParser: true,
          logging: true,
        };
      },
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

Console with Error:

[8:45:52 AM] File change detected. Starting incremental compilation...

[8:45:52 AM] Found 0 errors. Watching for file changes.

[Nest] 26331  - 08/23/2021, 8:45:53 AM     LOG [NestFactory] Starting Nest application...
[Nest] 26331  - 08/23/2021, 8:45:53 AM     LOG [InstanceLoader] AppModule dependencies initialized +26ms
[Nest] 26331  - 08/23/2021, 8:45:53 AM     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 26331  - 08/23/2021, 8:45:53 AM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms
[Nest] 26331  - 08/23/2021, 8:45:53 AM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 26331  - 08/23/2021, 8:45:53 AM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 26331  - 08/23/2021, 8:45:55 AM     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +2361ms
[Nest] 26331  - 08/23/2021, 8:45:55 AM     LOG [NestApplication] Nest application successfully started +5ms

/dir/to/project/node_modules/avvio/boot.js:533
      res = func()
            ^
TypeError: fastify.next is not a function
    at /dir/to/project/src/server/main.ts:30:54 ===> appConfig.staticRoutes.forEach((page) => fastify.next(page));

My temporary solution is to wait until the .next() function becomes available in the fastify instance and then run the code. But I don't think that this is the correct way of solving this issue.

Code:

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );

  await app.init();

  const configService = app.get<ConfigService<IConfigTypes>>(ConfigService);
  const appConfig = configService.get<IAppConfig>('app');

  const fastify = await app.register(fastifyNext, {
    dev: appConfig.isDev,
    dir: appConfig.clientPath,
  });

  await new Promise((res) => {
    const launch = async () => {
      if (!fastify.next) {
        setTimeout(launch, 200);
        return;
      }

      appConfig.staticRoutes.forEach((page) => fastify.next(page));
      await app.listen(appConfig.port);
      Logger.log(`🚀 Server is running on port ${appConfig.port}`, 'Bootstrap');
      res(null);
    };

    launch();
  });
}
bootstrap();

If I remove TypeOrmModule from AppModule the error disappears, I think the error is caused by the async loading of the Module forRootAsync.

Does anyone have any idea on how to fix it? If you know the better solution to solving this issue, please help me. Thank you!


Solution

  • The solution that I found is to get FastifyInstance from Nest.js HTTPAdapter and use that instance to register the plugin. Also, I used a promise to wait until fastify registers the plugin and then listen to the port (start the app).

      await app.init();
    
      const configService = app.get<ConfigService<IConfigTypes>>(ConfigService);
      const appConfig = configService.get<IAppConfig>('app');
      const fastify = app.getHttpAdapter().getInstance() as FastifyInstance;
    
      await new Promise((resolve) => {
        fastify
          .register(fastifyNext, {
            dev: appConfig.isDev,
            dir: appConfig.clientPath,
          })
          .after(async () => {
            appConfig.staticRoutes.forEach((route) => fastify.next(route));
            resolve(null);
          });
      });
    
      await app.listen(appConfig.port);
      Logger.log(`🚀 Server is running on port ${appConfig.port}`, 'Bootstrap');