typescript 在NestJS模块中使用配置服务的最佳做法

yzckvree  于 2022-12-24  发布在  TypeScript
关注(0)|答案(4)|浏览(200)

我想使用环境变量来配置每个模块的HttpModule,我可以从docs使用如下配置:

@Module({
  imports: [HttpModule.register({
    timeout: 5000,
    maxRedirects: 5,
  })],
})

但我不知道从环境变量(或配置服务)中包含baseURL的最佳实践是什么,例如:

@Module({
imports: [HttpModule.register({
    baseURL:  this.config.get('API_BASE_URL'),
    timeout: 5000,
    maxRedirects: 5,
})],

this.config在这里是undefined,因为它超出了类。
从环境变量(或配置服务)设置baseURL的最佳实践是什么?

jtjikinw

jtjikinw1#

1月19日更新

HttpModule.registerAsync()随此pull request添加到版本5.5.0中。

HttpModule.registerAsync({
  imports:[ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    baseURL:  configService.get('API_BASE_URL'),
    timeout: 5000,
    maxRedirects: 5,
  }),
  inject: [ConfigService]
}),

原始帖子

这个问题在本期中讨论过,对于TypeOrmModuleMongooseModule等nestjs模块,实现了以下模式。
useFactory方法返回配置对象。

TypeOrmModule.forRootAsync({
  imports:[ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    type: configService.getDatabase()
  }),
  inject: [ConfigService]
}),

尽管卡米尔写道
上述约定现在适用于所有嵌套模块,并将被视为最佳实践(+第三方模块的建议)。
它似乎还没有在HttpModule上实现,但也许你可以就此打开一个问题。在我上面提到的问题中还有一些其他的建议。
还可以查看官方的docs,其中包含关于如何实现ConfigService的最佳实践。

u5i3ibmn

u5i3ibmn2#

尽管这个问题的最高评分答案在技术上对大多数实现都是正确的,但是@nestjs/typeorm包和TypeOrmModule的用户应该使用更像下面的实现。

// NestJS expects database types to match a type listed in TypeOrmModuleOptions
import { TypeOrmModuleOptions } from '@nestjs/typeorm/dist/interfaces/typeorm-options.interface';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [mySettingsFactory],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        type: configService.get<TypeOrmModuleOptions>('database.type', {
          infer: true, // We also need to infer the type of the database.type variable to make userFactory happy
        }),
        database: configService.get<string>('database.host'),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: true,
        logging: true,
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [],
})
export class AppRoot {
  constructor(private connection: Connection) {}
}

这段代码的主要工作是从TypeORM中检索正确的类型(参见导入),并使用它们来提示configService.get()方法的返回值。如果您不使用正确的TypeORM类型,Typescript会发疯的。

nhn9ugyo

nhn9ugyo3#

我在实现ConfigService时也遇到了几个问题,如NestJS文档中所述(没有类型安全,没有配置值的模块化,. a....),我在这里详细写下了我们公司最终的NestJS配置管理策略:NestJS Configuration Management
基本思想是拥有一个中央配置模块,从进程的环境中加载所有配置值。然而,不是向所有模块提供单个服务,每个模块都可以注入一个专用的配置值子集!因此每个模块都包含一个类,该类指定了该模块在运行时需要提供的所有配置值。这同时为开发人员提供了类型-安全访问配置值(而不是在整个代码库中使用字符串文字)
希望此模式也适用于您的用例:)

fwzugrvs

fwzugrvs4#

@Kim克恩给出了很好的回答,它清楚地说明了将ConfigService注入到模块配置中,这可能依赖于环境变量;然而,从我个人的经验来看,你的app-root模块或者其他一些有几个导入的模块可能会变得拥挤和/或难以阅读,以及理解导入、模块配置和你定义的模块依赖于什么。所以,感谢Jay McDoniel,他策划了这个问题,you can move configuration logic into a separate file

第一个解决方案

app.module.ts的示例:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MikroOrmModule } from '@mikro-orm/nestjs';

import { AppService } from './users.service';
import { AppController } from './users.controller';
import { get_db_config } from './config/database.config';

@Module({
    imports:     [
        ConfigModule.forRoot({ 
            isGlobal:        true, 
            expandVariables: true,
        }),

        MikroOrmModule.forRootAsync( get_db_config() ),
    ],
    controllers: [AppController],
    providers:   [AppService],
})
export class AppModule {}

config/database.config.ts的示例:

import { MikroOrmModuleAsyncOptions } from "@mikro-orm/nestjs";
import { ConfigService } from "@nestjs/config";

export function get_db_config(): MikroOrmModuleAsyncOptions
{
    return {
        useFactory: (configService: ConfigService) => 
        ({
            dbName:          'driver',
            type:            'postgresql',
            host:             configService.get('DB_HOST'),
            port:             configService.get('DB_PORT'),
            user:             configService.get('DB_USERNAME'),
            password:         configService.get('DB_PASSWORD'),
            autoLoadEntities: true
        }),
        inject: [ConfigService]
    }
}

但是,NestJS文档-配置名称空间以及NestJS身份验证和授权课程提供了解决此问题的替代方法。

第二种溶液

auth.module.ts的示例:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { JwtModule } from '@nestjs/jwt';

import jwtConfig from './jwt.config';

@Module({
    imports: [
        ConfigModule.forFeature( jwtConfig ),
        JwtModule.registerAsync( jwtConfig.asProvider() ),
    ]
})
export class AuthModule {}

jwt.config.ts的示例:

import { registerAs } from "@nestjs/config"

export default registerAs('jwt', () => {
    return {
        secret:         process.env.JWT_SECRET,
        issuer:         process.env.JWT_TOKEN_ISSUER,
        accessTokenTtl: parseInt(process.env.JWT_TOKEN_TTL)
    };
});

相关问题