NestJS如何使用.env和@nestjs/config配置TypeOrm连接?

10

我正在尝试找到最合法的方法,使用.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无法解析TypeOrmConfigService的依赖项(?)。 请确保索引[0]处的参数在TypeOrmCoreModule上下文中可用。 如何修复它? 或者(最好的方式)是否有任何NestJs + TypeOrm + @nestjs/config + .env(位于repo之外,带有DATABASE_PASSWORD)+ config(我指处理config/development.yml、config/production.yml等的npm包配置文件)的示例?
看起来我正在寻找一个非常标准的东西,即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...
13个回答

14

这是我在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=

10

在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 {}

on 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 {}

这个对我有效


5
我遇到了同样的问题,我只是添加了以下内容:
import 'dotenv/config';

我的app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { typeOrmConfig } from '../ormconfig';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ConfigModule.forRoot(), TypeOrmModule.forRoot(typeOrmConfig)],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

我的 ormconfig 配置如下:

import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import 'dotenv/config'; // <- this line is the important

const config: TypeOrmModuleOptions = {
  type: 'postgres',
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT),
  username: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
};
export const typeOrmConfig = config;

3
你的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 的原因,因为后者支持导入属性,但仍然有些东西缺失。 - Kasheftin
当您使用 @Injectable() 而不是 @Module() 时,您会遇到什么错误?这应该是一个提供程序,因此应该是 @Injectable()。我以前用过这种模式。 - Jay McDoniel
错误与之前完全相同,Nest 无法解析 TypeOrmConfigService 的依赖项。请确保索引 [0] 处的参数在 TypeOrmCoreModule 上下文中可用。也许 isGlobal 的工作方式不如预期? - Kasheftin
你能把完整的错误信息添加到原始问题中吗? - Jay McDoniel
完成。完成。完成。 - Kasheftin
你好奇的是,你正在运行哪个版本的Nest?我好久没见过那样的错误了。 - Jay McDoniel

2

简单易行的方法

使用npm安装@nestjs/config模块。

 @Module({
  imports: [
    ConfigModule.forRoot(),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      name: process.env.DATABASE_NAME,
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        return {
          type: 'mysql',
          host: configService.get(`DATABASE_HOST`),
          port: configService.get(`DATABASE_PORT`),
          username: configService.get('DB_USER'),
          database: configService.get('DATABASE_NAME'),
          password: configService.get('DB_PASSWORD'),
          entities: [__dirname + '/../entities/*.entity{.ts,.js}'],
          synchronize: false,
          logging: true,
        };
      },
    }),
  ],

你为什么在 useFactory 中使用 async - Shafi

2

不确定是否仍然相关,但我认为这就是问题所在。

你看到的“Nest无法解析...”错误意味着你试图使用依赖注入来注入一个类,但是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 {}

2

你可以尝试这个:

// 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 {}

1

对于我而言,我只是将这个作为我的示例添加进来。

import { Injectable } from '@nestjs/common';
import { TypeOrmOptionsFactory, TypeOrmModuleOptions } from '@nestjs/typeorm';
import{Config} from'./config'
import { ConfigService } from '@nestjs/config';

@Injectable()
export class DatabaseConnectionService implements TypeOrmOptionsFactory {
  constructor(private configService: ConfigService) {}

  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      name: 'default',
      type: 'postgres',
      host: this.configService.get<string>("DATABASE_HOST"),
      port: Number(this.configService.get<number>("DATABASE_PORT")) ,
      username: this.configService.get<string>("DATABASE_USERNAME"),
      password: this.configService.get<string>("DATABASE_PASSWORD"),
      database: this.configService.get<string>("DATABASE_DB"),
      synchronize: true,
      sslmode:"disable",
      logging: true,  
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
    
      migrations: [__dirname + "/migrations/**/*{.ts,.js}"],
      migrationsRun: false,
      maxQueryExecutionTime: 0.1 /** To log request runtime */,
      cli: {
        migrationsDir: __dirname + "/migrations/**/*{.ts,.js}",
      },
      
      
    }
  }
}

在 app.module.ts 文件中,只需在 import 区域添加以下代码

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

    TypeOrmModule.forRootAsync({
      useClass: DatabaseConnectionService,
    })]

1
你可以将它分配到另一个模块中 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 是环境文件,或者您可以直接将其放在 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 {}

最后一种方法对我有效。谢谢! 你也可以使用以下代码: type: config.get<any>('TYPE') (在环境文件中:TYPE=postgres) synchronize: config.get<boolean>('SYNCHRONIZE') (在环境文件中:SYNCHRONIZE=true) - bendeg

1
你可以使用 "nestjs-easyconfig",我用它来处理多个 .env 文件(例如:.env.development、.env.production),它与 nestjs/config 几乎相同。你可以查看我的 GitHub 存储库

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接