简体   繁体   English

在 NestJS 模块中使用配置服务的最佳实践

[英]Best practice to use config service in NestJS Module

I want to use environment variables to configure the HttpModule per module, from the docs I can use the configuration like this:我想使用环境变量来配置每个模块的HttpModule ,从文档中我可以使用这样的配置:

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

But I don't know what is the best practice to inclue a baseURL from environment vairable (or a config service), for example like this:但是我不知道从环境 vairable(或配置服务)中包含 baseURL 的最佳做法是什么,例如:

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

The this.config is undefined here cause it's out of class. this.config在这里undefined ,因为它不在课堂上。

What is the best practice to set baseURL from environment variables (or config service)?从环境变量(或配置服务)设置 baseURL 的最佳做法是什么?

Update Jan 19 1月19日更新

HttpModule.registerAsync() was added in version 5.5.0 with this pull request . HttpModule.registerAsync()是在 5.5.0 版本中添加的,带有这个pull request

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

Original Post原帖

This problem was discussed in this issue .这个问题在这个问题上进行了讨论。 For the nestjs modules like the TypeOrmModule or the MongooseModule the following pattern was implemented.对于像TypeOrmModuleMongooseModule这样的 nestjs 模块,实现了以下模式。

The useFactory method returns the configuration object. useFactory方法返回配置对象。

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

Although Kamil wrote虽然卡米尔写道

Above convention is now applied in all nest modules and will be treated as a best practice (+recommendation for 3rd party modules).上述约定现在适用于所有嵌套模块,并将被视为最佳实践(+对第 3 方模块的推荐)。 More in the docs更多在文档中

it does not seem to be implemented for the HttpModule yet, but maybe you can open an issue about it.它似乎还没有为HttpModule实现,但也许你可以打开一个关于它的问题。 There are also some other suggestions in the issue I mentioned above.我上面提到的问题中还有一些其他的建议。

Also have a look at the official docs with best practices on how to implement a ConfigService .还可以查看官方文档,了解如何实现ConfigService的最佳实践。

Although the top rated answer to this question is technically correct for most implementations, users of the @nestjs/typeorm package, and the TypeOrmModule should use an implementation that looks more like the below.尽管对于大多数实现来说,这个问题的最高评价答案在技术上是正确的,但@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) {}
}

The major thing this code is doing is retrieving the correct typings from TypeORM (see the import) and using them to hint the return value configService.get() method.这段代码所做的主要事情是从 TypeORM 中检索正确的类型(参见导入)并使用它们来提示返回值 configService.get() 方法。 If you don't use the correct TypeORM typings, Typescript would get mad.如果你不使用正确的 TypeORM 类型,Typescript 会发疯的。

I also encountered several issues with implementing a ConfigService as described in the NestJS documentation (no type-safety, no modularity of configuration values, ...), I wrote down our company's final NestJS configuration management strategy in great detail here: NestJS Configuration Management我还遇到了实现ConfigService的几个问题,如 NestJS 文档中所述(没有类型安全性,没有配置值的模块化,...),我在这里非常详细地写下了我们公司的最终 NestJS 配置管理策略: NestJS 配置管理

The basic idea is to have a central config module that loads all configuration values from the processes' environment.基本思想是有一个中央配置模块,从进程环境中加载所有配置值。 However, instead of providing a single service to all modules, each module can inject a dedicated subset of the configuration values!但是,不是为所有模块提供单一服务,而是每个模块都可以注入配置值的专用子集! So each module contains a class that specifies all configuration values that this module needs to be provided at runtime.因此,每个模块都包含一个类,该类指定该模块在运行时需要提供的所有配置值。 This simultaneously gives the developer type-safe access to configuration values (instead of using string literals throughout the codebase)这同时为开发人员提供了对配置值的类型安全访问(而不是在整个代码库中使用字符串文字)

Hope this pattern also works for your use-case :)希望这种模式也适用于您的用例:)

Great answer by @Kim Kern , which clearly goes over injection of the ConfigService into a module configuration, that might be dependent on environment variables; @Kim Kern的好回答,它清楚地将ConfigService注入模块配置,这可能依赖于环境变量; however, from my personal experience, your app-root module or some other module with a couple of imports might get crowded and/or hard to read as well as understand the imports, module configuration and what the module you are defining relies on.然而,根据我的个人经验,你的 app-root 模块或一些其他带有几个导入的模块可能会变得拥挤和/或难以阅读以及理解导入、模块配置和你定义的模块所依赖的内容。 So, thanks to Jay McDoniel , who curated me on this question, you can move configuration logic into a separate file .因此,感谢Jay McDoniel为我策划了这个问题, you can move configuration logic into a separate file中。



First Solution第一个解决方案


Example of app.module.ts : 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 {}

Example of config/database.config.ts : 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]
    }
}



However, NestJS Docs - Configuration Namespaces as well as NestJS Authentication and Authorization Course provide an alternative method of solving this issue.但是, NestJS Docs - Configuration Namespaces以及 NestJS Authentication and Authorization Course 提供了解决此问题的替代方法。



Second Solution第二种解决方案


Example of auth.module.ts : 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 {}

Example of jwt.config.ts : 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)
    };
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM