JBCP
JBCP

Reputation: 13485

How to inject the path configuration into ServeStaticModule from another service?

The NestJS documentation says to serve static files like this:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'client'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

But as a DI and SOLID practitioner, I want to make the rootPath configurable. Lets say I have a ConfigModule or my own ConstantsModule. How do I inject rootPath in a way similar to this?

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: this.configService.get<string>('staticRootPath'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Upvotes: 4

Views: 1883

Answers (4)

Maz T
Maz T

Reputation: 1244

I've solved it by implementing ServeStaticModuleOptionsFactory.

serve-static-configuration.module.ts

import { Module } from '@nestjs/common';
import { ConfigurationModule } from '../configuration/configuration.module';
import { ConfigurationService } from '../configuration/configuration.service';
import { ServeStaticConfigurationService } from './serve-static-configuration.service';

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

serve-static-configuration.service.ts

import { Injectable } from '@nestjs/common';
import { ServeStaticModuleOptions, ServeStaticModuleOptionsFactory } from '@nestjs/serve-static';
import { ConfigurationService } from '../configuration/configuration.service';

@Injectable()
export class ServeStaticConfigurationService implements ServeStaticModuleOptionsFactory {
    constructor(private configurationService: ConfigurationService) { }

    createLoggerOptions(): ServeStaticModuleOptions[] | Promise<ServeStaticModuleOptions[]> {
        return [{
            rootPath: this.configurationService.wwwRoot,
            exclude: [`/api*`]
        }];
    }
}

app.module.ts

import { Module } from '@nestjs/common';
import { ServeStaticModule } from '@nestjs/serve-static';
import { AppController } from './app.controller';
import { ServeStaticConfigurationModule } from './serve-static-configuration/serve-static-configuration.module';
import { ServeStaticConfigurationService } from './serve-static-configuration/serve-static-configuration.service';

@Module({
    imports: [
        ServeStaticModule.forRootAsync({
            imports: [ServeStaticConfigurationModule],
            useExisting: ServeStaticConfigurationService
        })
    ],
    controllers: [AppController]
})
export class AppModule { }

Upvotes: 0

Doan Quang Tuyen
Doan Quang Tuyen

Reputation: 1

It must be an array, as declare in code:

useFactory?: (...args: any[])

Upvotes: 0

Aleksa
Aleksa

Reputation: 148

The correct answer:

ServeStaticModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => [{
          rootPath: join(__dirname, '..', configService.get<string>('STATIC_FOLDER')),
      }]
})

Upvotes: 6

leonardfactory
leonardfactory

Reputation: 3501

Even if it is not documented, you can use the forRootAsync, it is typical for NestJS modules to have this version which allows you to inject dependencies and/or do async configuration:

@Module({
  imports: [
    ServeStaticModule.forRootAsync({
      imports: [ConfigModule],
      injects: [ConfigService],
      useFactory: (configService) => ({
        rootPath: configService.get<string>('staticRootPath')
      })
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

For reference, found it in the GitHub sources

Upvotes: 1

Related Questions