I'm trying to create a custom NestJS module. I'm providing 4 different methods forRoot, forRootAsync, forFeature, forFeatureAsync, although I'm using only two of it forRootAsync and forFeatureAsync.
I've imported module in AppModule and configured using forRootAsync, then also imported same module in a feature module UploadModule and configured using forFeatureAsync.
The problem is that undesirably forFeatureAsync is being invoked before forRootAsync. Mean my module is first initialized in UploadModule and then in AppModule, although purposefully it should do exactly opposite of it.
Please pay an attention to the source code of FileStorageService. This service should desirably first configure the baseDir - base directory of every upload. Then in feature, or even for the app module it should create every directory provided in the options.
Custom Module Source Code
file-storage.module.ts
import { DynamicModule, Module } from '@nestjs/common';
import { FileStorageService } from './file-storage.service';
import {
  ASYNC_OPTIONS_TYPE,
  MODULE_OPTIONS_TOKEN,
  OPTIONS_TYPE,
} from './file-storage.module-defination';
@Module({
  providers: [],
  exports: [],
})
export class FileStorageModule {
  static forRoot(options: typeof OPTIONS_TYPE): DynamicModule {
    // omit for better clarity
  }
  static forRootAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
    return {
      module: FileStorageModule,
      imports: options.imports,
      providers: [
        {
          provide: MODULE_OPTIONS_TOKEN,
          useClass: options.useClass,
          useFactory: options.useFactory,
          inject: options.inject,
        },
        FileStorageService,
      ],
      exports: [FileStorageService],
    };
  }
  static forFeature(options: typeof OPTIONS_TYPE): DynamicModule {
    // omit for better clarity
  }
  static forFeatureAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
    return {
      module: FileStorageModule,
      imports: options.imports,
      providers: [
        {
          provide: MODULE_OPTIONS_TOKEN,
          useClass: options.useClass,
          useFactory: options.useFactory,
          inject: options.inject,
        },
        FileStorageService,
      ],
      exports: [FileStorageService],
    };
  }
}
file-storage.service.ts
import { Inject, Injectable, Logger } from '@nestjs/common';
import {
  FileStorageModuleOptions,
  MODULE_OPTIONS_TOKEN,
} from './file-storage.module-defination';
import * as path from 'path';
import { mkdirSync, statSync } from 'fs';
@Injectable()
export class FileStorageService {
  private readonly logger = new Logger(FileStorageService.name);
  private static baseDir = '';
  directories = new Map<string, string>();
  constructor(
    @Inject(MODULE_OPTIONS_TOKEN) private options: FileStorageModuleOptions,
  ) {
    this.logger.log(options);
    if (options.baseDirectory) {
      this.logger.log(`Base Directory: ${options.baseDirectory}`);
      FileStorageService.baseDir = options.baseDirectory;
      syncDirectory(this.baseDirectory);
    }
    if (!options.directories?.length) return;
    for (const dir of options.directories) {
      try {
        let directory = this.baseDirectory;
        if (typeof dir.path == 'string') {
          directory = path.join(directory, dir.path);
        } else {
          for (const p of dir.path) {
            directory = path.join(directory, p);
          }
        }
        syncDirectory(directory);
        this.directories.set(dir.name, directory);
        this.logger.log(
          `Synced Directory: ${dir.name} | ${dir.path} | ${directory}`,
        );
      } catch {
        this.logger.warn(`Error Syncing Directory: ${dir.name} | ${dir.path}`);
      }
    }
  }
}
function syncDirectory(dir: string) {
  try {
    mkdirSync(dir, { recursive: true });
  } catch (err) {
    // omit for better clarity
  }
}
AppModule and UploadModule Source Code
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SequelizeModule } from '@nestjs/sequelize';
import { AuthModule } from './auth/auth.module';
import { SharedModule } from './shared/shared.module';
import { TodoModule } from './todo/todo.module';
import { User } from './shared/user.model';
import { Todo } from './todo/todo.model';
import { UploadModule } from './upload/upload.module';
import { FileStorageModule } from './file-storage/file-storage.module';
@Module({
  imports: [
    ..., // N modules imported
    FileStorageModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (config: ConfigService) => {
        return {
          baseDirectory: config.get('STORAGE_DIR'),
        };
      },
      inject: [ConfigService],
    }),
    ..., // N modules imported
    UploadModule, // It is definitely last in Queue
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
upload.module.ts
import { BadRequestException, Module } from '@nestjs/common';
import { MulterModule } from '@nestjs/platform-express';
import { UploadService } from './upload.service';
import { UploadController } from './upload.controller';
import { storageDirectories } from 'src/config';
import * as multer from 'multer';
import { v4 as uuidv4 } from 'uuid';
import * as mime from 'mime-types';
import { FileStorageModule } from 'src/file-storage/file-storage.module';
import { UploadDirectories } from './upload.interface';
@Module({
  imports: [
    ..., // N modules imported
    FileStorageModule.forFeatureAsync({
      useFactory: () => ({
        directories: [
          {
            name: UploadDirectories.ProfilePicture,
            path: UploadDirectories.ProfilePicture,
          },
        ],
      }),
    }),
  ],
  providers: [...],
  controllers: [...],
})
export class UploadModule {}
Logs of the application
Most of the logs are stripped, we're interested in the order of exection of-course.
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [NestFactory] Starting Nest application...
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] AppModule dependencies initialized +39ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] SequelizeModule dependencies initialized +0ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] PassportModule dependencies initialized +0ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] MulterModule dependencies initialized +1ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [FileStorageService] Object:
{
  "directories": [
    {
      "name": "user/profile-picture",
      "path": "user/profile-picture"
    }
  ]
}
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [FileStorageService] Synced Directory: user/profile-picture | user/profile-picture | user/profile-pict
ure
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] FileStorageModule dependencies initialized +0ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] ConfigModule dependencies initialized +2ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] UploadModule dependencies initialized +1ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [FileStorageService] Object:
{
  "baseDirectory": "/var/cool-todo"
}
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [FileStorageService] Base Directory: /var/cool-todo
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] FileStorageModule dependencies initialized +2ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader] JwtModule dependencies initialized +0ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [InstanceLoader]
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [RoutesResolver] UploadController {/api/v1/upload}: +0ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [RouterExplorer] Mapped {/api/v1/upload, POST} route +1ms
[Nest] 355  - 12/08/2023, 3:25:56 PM     LOG [NestApplication] Nest application successfully started +5ms
I've tried:
- Arrange module imports in 
AppModule, placed UploadModule afterFileStorageModule. Definitely I suspect thatAsynchas something to do with it, so no way out. - Googling, not found anywhere mentioned.
 
I expect:
- If issue is due to 
Async, explain the behavior, so I would never see myself cornered again. - Definitely the solution, I need to get out of it.
 - I cannot sacrifice async, as I need 
ConfigServicedependency, I want to set the storage directory from environment.