【问题标题】:NestJS setup TypeOrm connection with .env and @nestjs/configNestJS 使用 .env 和 @nestjs/config 设置 TypeOrm 连接
【发布时间】:2020-12-20 00:15:09
【问题描述】:

我正在尝试找到使用 .env 文件设置 NestJS 数据库的最合法方法。那就是我想使用@nestjs/config 包来导入.env 变量并在TypeOrmModule 中使用它们。

看来我需要使用TypeOrmModule.forRootAsync

我正在尝试这样做:

// app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    TypeOrmModule.forRootAsync({
      useClass: TypeOrmConfigService,
    }),
    ...
  ],

})
export class AppModule {}

然后,有TypeOrmConfigService

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';

@Module({
  imports: [ConfigModule],
})
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'mysql',
      host: this.configService.get('DATABASE_HOST'),
      username: this.configService.get('DATABASE_USERNAME'),
      password: this.configService.get('DATABASE_PASSWORD'),
    };
  }
}

最后一个不正确:Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context.

如何解决? 或者(最优选)在任何地方都有 NestJs + TypeOrm + @nestjs/config + .env (在 repo 之外,使用 DATABASE_PASSWORD)+ config 的示例(我的意思是处理 config/development.yml、config/ 的 npm 包配置production.yml 等)?

似乎我正在寻找一个非常标准的东西,hello world,它应该是每个 NestJS 项目的开始,但我发现将 @nestjs/config 和 TypeOrm 结合起来有困难。

更新。如果我把@Module换成@Injectable,报错是完全一样的:

yarn run v1.22.4
$ NODE_ENV=development nodemon
[nodemon] 1.19.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: /home/kasheftin/work/pubngn4/nestjs-course-task-management/src/**/*
[nodemon] starting `ts-node -r tsconfig-paths/register src/main.ts`
[Nest] 25384   - 09/01/2020, 8:07 PM   [NestFactory] Starting Nest application...
[Nest] 25384   - 09/01/2020, 8:07 PM   [InstanceLoader] AppModule dependencies initialized +11ms
[Nest] 25384   - 09/01/2020, 8:07 PM   [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 25384   - 09/01/2020, 8:07 PM   [InstanceLoader] PassportModule dependencies initialized +0ms
[Nest] 25384   - 09/01/2020, 8:07 PM   [ExceptionHandler] Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context. +1ms
Error: Nest can't resolve dependencies of the TypeOrmConfigService (?). Please make sure that the argument at index [0] is available in the TypeOrmCoreModule context.
    at Injector.lookupComponentInExports (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:180:19)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at Injector.resolveComponentInstance (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:143:33)
    at resolveParam (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:96:38)
    at async Promise.all (index 0)
    at Injector.resolveConstructorParams (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:112:27)
    at Injector.loadInstance (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:78:9)
    at Injector.loadProvider (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/injector.js:35:9)
    at async Promise.all (index 3)
    at InstanceLoader.createInstancesOfProviders (/home/kasheftin/work/pubngn4/nestjs-course-task-management/node_modules/@nestjs/core/injector/instance-loader.js:41:9)
 1: 0xa2afd0 node::Abort() [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 2: 0xa9e7a9  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 3: 0xc06bab  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 4: 0xc08156  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 5: 0xc087d6 v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
 6: 0x13a9f19  [/home/kasheftin/.nvm/versions/node/v14.3.0/bin/node]
Aborted (core dumped)
[nodemon] app crashed - waiting for file changes before starting...

【问题讨论】:

    标签: typescript nestjs typeorm dotenv


    【解决方案1】:

    这是我在 app.module.ts 中使用 Postgres 的工作脚本,但对于 MySQL 将非常相似。有时在重建之前需要手动删除 dist 文件夹 - 当数据库无法同步时。

    import { ConfigModule, ConfigService } from '@nestjs/config';
    
    
    @Module({
      imports: [
        ConfigModule.forRoot(),
        TypeOrmModule.forRootAsync({
          imports: [ConfigModule],      
          useFactory: (configService: ConfigService) => ({
            type: 'postgres' as 'postgres',
            host: configService.get<string>('DATABASE_HOST'),
            port: parseInt(configService.get<string>('DATABASE_PORT')),
            username: configService.get<string>('DATABASE_USER'),
            password: configService.get<string>('DATABASE_PASS'),
            database: configService.get<string>('DATABASE_NAME'),
            entities: [__dirname + '/**/*.entity{.ts,.js}'],
            synchronize: true,
          }),
          inject: [ConfigService],
        }),
    

    .env 在根文件夹中

    DATABASE_USER=
    DATABASE_PASS=
    DATABASE_HOST=
    DATABASE_NAME=
    DATABASE_PORT=
    

    【讨论】:

      【解决方案2】:

      不确定它是否仍然相关,但是,我认为这是正在发生的事情。

      您看到“Nest cant't resolve...”的错误意味着您正在尝试使用依赖注入来注入一个类,但是 Nest 在依赖项容器中找不到此依赖项。

      为了解决这个问题,你需要将ConfigService(解析失败的依赖)添加到app.module.ts Providers部分。

      类似的东西:

      @Module({
         imports: [
              ConfigModule.forRoot(),
              TypeOrmModule.forRoot({
                type: 'mysql',
                host: process.env.DATABASE_HOST,
                username: process.env.DATABASE_USERNAME,
                password: process.env.DATABASE_PASSWORD,
              }),
              ...
         ],
         providers: [
              ConfigService
         ]
          
      })
      export class AppModule {}
      

      【讨论】:

        【解决方案3】:

        您可以使用“nestjs-easyconfig”,我将它用于多个 .env(例如:.env.development、.env.production' 它与nestjs/config 几乎相同。您可以查看我的github repo

        【讨论】:

          【解决方案4】:

          您的TypeOrmConfigService 不应是@Module()。应该是@Injectable()。其他一切看起来都很好。

          import { Injectable } from '@nestjs/common';
          import { ConfigService } from '@nestjs/config';
          import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
          
          @Injectable()
          export class TypeOrmConfigService implements TypeOrmOptionsFactory {
            constructor(private configService: ConfigService) {}
          
            createTypeOrmOptions(): TypeOrmModuleOptions {
              return {
                type: 'mysql',
                host: this.configService.get('DATABASE_HOST'),
                username: this.configService.get('DATABASE_USERNAME'),
                password: this.configService.get('DATABASE_PASSWORD'),
              };
            }
          }
          

          【讨论】:

          • 遗憾的是这不起作用。错误是一样的。似乎配置服务没有传递给构造函数。这就是为什么我尝试将 @Injectable 转换为 @Module 的原因,因为最后一个支持 import prop,但这里仍然缺少一些东西。
          • 当您使用@Injectable() 而不是@Module() 时出现什么错误?这应该是provider,所以它应该是@Injectable()。我以前用过这种模式
          • 错误一模一样,Nest无法解析TypeOrmConfigService(?)的依赖。请确保索引 [0] 处的参数在 TypeOrmCoreModule 上下文中可用。也许 isGlobal 没有按预期工作?
          • 您能否将完整的错误添加到原始问题中?
          • 完成。完毕。完成。
          【解决方案5】:

          你可以试试这个:

          // app.module.ts
          
          @Module({
            imports: [
              ConfigModule.forRoot(),
              TypeOrmModule.forRoot({
                type: 'mysql',
                host: process.env.DATABASE_HOST,
                username: process.env.DATABASE_USERNAME,
                password: process.env.DATABASE_PASSWORD,
              }),
              ...
            ],
          
          })
          export class AppModule {}
          

          【讨论】:

            【解决方案6】:

            创建一个文件(在本例中文件是database.provider.ts),该文件将导出一个连接数组。

            import { ConfigModule, ConfigService } from '@nestjs/config';
            import { TypeOrmModule } from '@nestjs/typeorm';
            export const databaseProviders = [
              TypeOrmModule.forRootAsync({
                imports: [ConfigModule],
                inject: [ConfigService],
                useFactory: (configService: ConfigService) => ({
                  type: 'postgres',
                  host: configService.get('PGHOST'),
                  port: +configService.get<number>('PGPORT'),
                  username: configService.get('PGUSER'),
                  password: configService.get('PGPASSWORD'),
                  database: configService.get('PGDATABASE'),
                  entities: ['dist/**/*.entity{.ts,.js}'],
                  synchronize: false,
                  logging: true,
                }),
              }),
            ];

            然后在你的database.module.ts中导入它

            import { databaseProviders } from './database.provider';
            import { DatabaseService } from './database.service';
            import { Module } from '@nestjs/common';
            @Module({
              imports: [...databaseProviders],
              providers: [DatabaseService],
              exports: [...databaseProviders],
            })
            export class DatabaseModule {}

            仅此而已,您可以根据需要在 database.provider.ts 中添加多个连接,此外,不要忘记在根模块中创建 .env 并导入数据库模块。

            【讨论】:

              【解决方案7】:

              你可以把它吐到另一个模块 typeorm.module.ts

              import { ConfigService } from '@nestjs/config';
              import { Module } from '@nestjs/common';
              import { TypeOrmModule } from '@nestjs/typeorm';
              
              @Module({
                imports: [
                  TypeOrmModule.forRootAsync({
                    useFactory: async (config: ConfigService) => ({
                      database: config.get<string>('DATABASE'),
                      username: config.get<string>('USERNAME'),
                      password: config.get<string>('PASSWORD'),
                      host: config.get<string>('HOST'),
                      port: parseInt(config.get('PORT')),
                      autoLoadEntities: true,
                      synchronize: true,
                      type: 'postgres',
                    }),
                    inject: [ConfigService],
                  }),
                ],
                controllers: [],
                providers: [],
              })
              export class TypeormConfigModule {}
              
              

              主 app.module.ts 像这样导入*

              import { TypeormConfigModule } from './config/database/typeorm.module';
              import { Module } from '@nestjs/common';
              import { ConfigModule, ConfigService } from '@nestjs/config';
              
              
              @Module({
                imports: [
                  CaslModule,
                  TypeormConfigModule,
                  ConfigModule.forRoot({
                    envFilePath: ['env/dev.env', 'env/prod.env', 'env/rom.env'],
                    isGlobal: true,
                  }),
                
                ],
              })
              export class AppModule {}
              
              
              

              ** env 是文件夹名称,prod.env 是 env 文件,也可以直接放在 app.module.ts 中,如上**

              另一种在 APP.MODULES.TS 中直接使用 ENV 的方法

              @Module({
                imports: [
                  CaslModule,
                 TypeOrmModule.forRootAsync({
                    useFactory: async (config: ConfigService) => ({
                      database: config.get<string>('DATABASE'),
                      username: config.get<string>('USERNAME'),
                      password: config.get<string>('PASSWORD'),
                      host: config.get<string>('HOST'),
                      port: parseInt(config.get('PORT')),
                      autoLoadEntities: true,
                      synchronize: true,
                      type: 'postgres',
                    }),
                    inject: [ConfigService],
                  }),
              ]
              }),
                  ConfigModule.forRoot({
                    envFilePath: ['env/dev.env', 'env/prod.env', 'env/rom.env'],
                    isGlobal: true,
                  }),
                
                ],
              })
              export class AppModule {}
              

              【讨论】:

                【解决方案8】:

                关于database.module.ts

                import { Module } from '@nestjs/common';
                import { TypeOrmModule } from '@nestjs/typeorm';
                import { ConfigModule, ConfigService } from '@nestjs/config';
                
                @Module({
                  imports: [
                    TypeOrmModule.forRootAsync({
                      imports: [ConfigModule],
                      inject: [ConfigService],
                      useFactory: (configService: ConfigService) => ({
                        type: 'mysql',
                        host: configService.get('DATABASE_HOST'),
                        port: configService.get('DATABASE_PORT'),
                        username: configService.get('DATABASE_USERNAME'),
                        password: configService.get('DATABASE_PASSWORD'),
                        database: configService.get('DATABASE_NAME'),
                        entities: ['dist/**/*.entity.js'],
                        synchronize: true,
                      }),
                    }),
                  ],
                })
                export class DatabaseModule {}
                

                在 app.module.ts 上

                import { DatabaseModule } from './database/database.module';
                import { ConfigModule } from '@nestjs/config';
                import { Module } from '@nestjs/common';
                
                @Module({
                  imports: [
                    ConfigModule.forRoot({}),
                    DatabaseModule,
                  ],
                })
                export class AppModule {}
                

                这对我有用

                【讨论】:

                  猜你喜欢
                  • 2019-06-19
                  • 2020-09-22
                  • 2021-12-24
                  • 2019-08-13
                  • 2022-01-21
                  • 2020-09-13
                  • 2020-12-09
                  • 2020-01-25
                  • 2021-04-28
                  相关资源
                  最近更新 更多