Поведение перехвата жизненного цикла NestJS onModuleInit для асинхронных провайдеров

#nestjs

#nestjs

Вопрос:

Я пытаюсь инициализировать a ConfigurationService с помощью onModuleInit перехвата. Ссылаясь на страницу перехватов жизненного цикла NestJS, я ожидаю, что перехват будет вызван при создании модуля. Однако это не так. В принципе, у меня есть это:

ConfigurationService:

 import { Injectable, OnModuleInit } from '@nestjs/common';
import { ApplicationConfiguration } from './interfaces/application-configuration.interface';
import { ServerConfiguration } from './interfaces/server-configuration.interface';
import * as path from 'path';

@Injectable()
export class ConfigurationService implements OnModuleInit {
    private _configuration: ApplicationConfiguration;

    public async getServerConfigurationAsync(): Promise<ServerConfiguration> {
        await this.ensureConfigurationIsLoadedAsync();

        return new Promise<ServerConfiguration>((resolve, reject) => {
            const serverConfiguration = this._configuration.server;

            if (!serverConfiguration) {
                reject(new Error('Missing server configuration.'));
            }

            return resolve(serverConfiguration);
        });
    }

    private async ensureConfigurationIsLoadedAsync(): Promise<void> {
        if (!this._configuration) {
            this._configuration = await this.fetchAppConfigurationAsync();
        }
    }

    private fetchAppConfigurationAsync(): Promise<ApplicationConfiguration> {
        const configDir = process.env.CONFIG_DIR;

        let configurationPath: string;

        if (configDir) {
            configurationPath = path.resolve(`configuration/${configDir}/config.json`);
        }
        else {
            configurationPath = path.resolve('configuration/config.json');
        }

        return new Promise<ApplicationConfiguration>((resolve, reject) => {
            try {
                const configuration = require(configurationPath);

                const mappedConfig: ApplicationConfiguration = {
                    server: configuration.server
                };

                resolve(mappedConfig);
            }
            catch (error) {
                console.log(`Can't fetch configuration at ${configurationPath}.`);

                reject(error);
            }
        });
    }

    // public async onModuleInit(): Promise<void> {
    //     console.log('ConfigurationService onModuleInit called.');

    //     await this.ensureConfigurationIsLoadedAsync();
    // }

    public  onModuleInit(): void {
        console.log('ConfigurationService onModuleInit called.');
    }
}
  

Конкретный асинхронный провайдер:

 export const SERVER_CONFIGURATION_PROVIDER: Provider = {
    provide: CONFIGURATION_TOKENS.SERVER_CONFIGURATION,
    useFactory: async (configurationService: ConfigurationService): Promise<ServerConfiguration> => {
        console.log('SERVER_CONFIGURATION_PROVIDER useFactory called.');

        return await configurationService.getServerConfigurationAsync();
    },
    inject: [ConfigurationService]
}
  

Модуль конфигурации:

 @Module({
  providers: [
    ConfigurationService,
    SERVER_CONFIGURATION_PROVIDER
  ],
  exports: [
    SERVER_CONFIGURATION_PROVIDER
  ]
})
export class ConfigurationModule { 

  // public async onModuleInit(): Promise<void> {
    //     console.log('ConfigurationModule onModuleInit called.');
    // }
  public onModuleInit(): void {
        console.log('ConfigurationModule onModuleInit called.');
    }
    }
  

И вот как я пытаюсь его использовать:

 @Controller('app')
export class AppController {
    private _serverConfig: ServerConfiguration;

    constructor(@Inject(CONFIGURATION_TOKENS.SERVER_CONFIGURATION) serverConfig: ServerConfiguration) {
        this._serverConfig = serverConfig;
    }

    @Get()
    public async getResult(): Promise<any> {
        return this._serverConfig;
    }
}
  

Теперь дело в том, что я хочу отменить await this.ensureConfigurationIsLoadedAsync(); вызов в getServerConfigurationAsync() методе и инициализировать конфигурацию через onModuleInit перехват. Однако при запуске приложения перехват вызывается задолго до ConfigurationService того, как он используется асинхронным провайдером. Я что-то упустил? Не следует ли вызывать перехват до того, как служба будет использована? В документации говорится:

Вызывается после разрешения зависимостей хост-модуля.

И

Для каждого модуля после инициализации модуля:

ожидание дочернего контроллера и методов onModuleInit() поставщика

метод ожидания модуля onModuleInit()

Тем не менее, я также нашел это в исходном коде:

nest/packages/core/nest-application.ts

 public async init(): Promise<this> {
    this.applyOptions();
    await this.httpAdapter?.init();

    const useBodyParser =
      this.appOptions amp;amp; this.appOptions.bodyParser !== false;
    useBodyParser amp;amp; this.registerParserMiddleware();

    await this.registerModules();
    await this.registerRouter();
    await this.callInitHook();
    await this.registerRouterHooks();
    await this.callBootstrapHook();

    this.isInitialized = true;
    this.logger.log(MESSAGES.APPLICATION_READY);
    return this;
}
  

Поэтому я не уверен, чего ожидать, и правильно ли я понимаю жизненный цикл. Вот мой журнал при запуске приложения:

 Before app create
[Nest] 28132   - 10/05/2020, 4:12:38 PM   [NestFactory] Starting Nest application...
SERVER_CONFIGURATION_PROVIDER useFactory called.
[Nest] 28132   - 10/05/2020, 4:12:38 PM   [InstanceLoader] ConfigurationModule dependencies initialized  10ms
[Nest] 28132   - 10/05/2020, 4:12:38 PM   [InstanceLoader] AppModule dependencies initialized  1ms
After app create
before listen
[Nest] 28132   - 10/05/2020, 4:12:38 PM   [RoutesResolver] AppController {/api/app}:  5ms
[Nest] 28132   - 10/05/2020, 4:12:38 PM   [RouterExplorer] Mapped {/api/app, GET} route  4ms
ConfigurationService onModuleInit called.
ConfigurationModule onModuleInit called.
[Nest] 28132   - 10/05/2020, 4:12:38 PM   [NestApplication] Nest application successfully started  2ms
after listen
Listening at port 5000.
  

Комментарии:

1. the hook gets called long after the ConfigurationService gets consumed by the async provider. Am I missing something? Shouldn't the hook be invoked before the service gets consumed? Что вы подразумеваете под «потреблять»? Сначала происходит создание экземпляра и внедрение. Затем он выполняет обратный цикл и вызывает onModuleInit.