web-dev-qa-db-ja.com

CLIおよびNESTJSアプリケーション用の1つの設定でTypeORMを設定する

私は私のNESTJSアプリケーションでTypeormを使用しています。 My app.module.tsには非常に標準の設定と機能があります。

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigService } from './config/config.service';
import { ConfigModule } from './config/config.module';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],

      // @ts-ignore issues with the type of the database
      useFactory: async (configService: ConfigService) => ({
        type: configService.getDBType(),
        Host: configService.getDBHost(),
        port: configService.getDBPort(),
        username: configService.getDBUser(),
        password: configService.getDBPassword(),
        database: configService.getDBName(),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: true,
      }),
      inject: [ConfigService],
    }),
    ConfigModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

つまりね。 CLI上の移行を実行したい場合は、ormconfig.jsを持つ必要があります。 ormconfig.jsconfig.service.jsの両方で資格情報を複製したくない。次のような.envファイルを作成しました。

TYPEORM_CONNECTION = mysql
TYPEORM_Host = app-db
TYPEORM_USERNAME = user
TYPEORM_PASSWORD = password
TYPEORM_DATABASE = db-dev
TYPEORM_PORT = 3306
TYPEORM_SYNCHRONIZE = true
TYPEORM_LOGGING = true
TYPEORM_ENTITIES = src/**/*.ts
TYPEORM_MIGRATIONS = src/migrations/**/*.ts
TYPEORM_MIGRATIONS_TABLE_NAME = migrations

ENV varsはここに示されているように定義されているので: Typeomのドキュメント 、私は先に進み、app.module.ts次のようになります。

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

DATABASE_Hostを使用すると、env vars DATABASE_PORTtypeorm cliなどが見つからないというエラーが発生しました。

これが私のconfig.service.tsです

import * as dotenv from 'dotenv';
import * as Joi from '@hapi/joi';
import * as fs from 'fs';
import { Injectable } from '@nestjs/common';
import { keys, pick } from 'lodash';

export type EnvConfig = Record<string, string>;

@Injectable()
export class ConfigService {
  private readonly envConfig: Record<string, string>;

  constructor(filePath: string) {
    const envNames = keys(this.getJoiObject());
    const envFromProcess = pick(process.env, envNames);
    const envFromFile = fs.existsSync(filePath) ? dotenv.parse(fs.readFileSync(filePath)) : {};
    const envConfig = Object.assign(envFromFile, envFromProcess);

    this.envConfig = this.validateInput(envConfig);
  }

  private validateInput(envConfig: EnvConfig): EnvConfig {
    const envVarsSchema: Joi.ObjectSchema = Joi.object(this.getJoiObject());

    const { error, value: validatedEnvConfig } = envVarsSchema.validate(envConfig);

    if (error) {
      throw new Error(`Config validation error: ${error.message}`);
    }

    return validatedEnvConfig;
  }

  private getJoiObject(): object {
    return {
      NODE_ENV: Joi.string()
        .valid('development', 'production', 'test', 'provision')
        .default('development'),
      PORT: Joi.number().default(3000),

      DATABASE_TYPE: Joi.string()
        .valid('mysql')
        .default('mysql'),

      DATABASE_Host: Joi.string().required(),
      DATABASE_PORT: Joi.number().required(),
      DATABASE_NAME: Joi.string().required(),
      DATABASE_USER: Joi.string().required(),
      DATABASE_PASSWORD: Joi.string().required(),
    };
  }

  get(key: string): string {
    return this.envConfig[key];
  }

  getPort(): number {
    return parseInt(this.envConfig.PORT, 10);
  }

  getDBType(): string {
    return this.envConfig.DATABASE_TYPE;
  }

  getDBHost(): string {
    return this.envConfig.DATABASE_Host;
  }

  getDBPort(): number {
    return parseInt(this.envConfig.DATABASE_PORT, 10);
  }

  getDBName(): string {
    return this.envConfig.DATABASE_NAME;
  }

  getDBUser(): string {
    return this.envConfig.DATABASE_USER;
  }

  getDBPassword(): string {
    return this.envConfig.DATABASE_PASSWORD;
  }
}

TYPEORM_ env varsは、ここで相互に排他的ですか?私たちは、CLIでTypeORMの作業を持つため、およびNESTJSアプリケーションのコンテキストで、環境変数をDATABASE_フォームに複製する必要がありますか?これはとても間違っているようです。これらの変数を複製する必要なしに、TypeORMを作成する正しい方法はCLI(開発中の移行にこれを望んでいます)、NESTJSアプリケーションでは何ですか?

6
randombits

解決

このソリューションでは、コードの重複を実行せずに、CLIの使用法とアプリケーション使用量の両方に同じパラメータを使用できます。

Path.join()を使用:

config.service.ts

_import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { join } from 'path';

// tslint:disable-next-line: no-var-requires
require('dotenv').config();

class ConfigService {

   constructor(private env: { [k: string]: string | undefined }) {}

   //...etc

  public getTypeOrmConfig(): TypeOrmModuleOptions {
    return {

      // obviously, change these if you're using a different DB
      type: 'postgres',
      Host: this.getValue('POSTGRES_Host'),
      port: Number(this.getValue('POSTGRES_PORT')),
      username: this.getValue('POSTGRES_USER'),
      password: this.getValue('POSTGRES_PASSWORD'),
      database: this.getValue('POSTGRES_DB'),

      entities: [join(__dirname, '**', '*.entity.{ts,js}')],

      migrationsTableName: 'migration',
      migrations: [join(__dirname, '..', 'migrations', '*.ts')],

      cli: {
        migrationsDir: '../migrations',
      },

      synchronize: true,
      ssl: this.isProduction(),
    };
  }
}

const configService = new ConfigService(process.env);

export default configService;
_

app.module.ts

引数なしでTypeOrmModule.forRoot()を使用している場合、これはデフォルトでプロジェクトのルートで_ormconfig.json_ファイルを探します。 TypeOmModuleOptionsパラメータと同様にそれを提供することができます。私はRiajul IslamとMuhammad Zeeshanとまさにこれをすることをすることをお勧めします。

_@Module({
    imports: [
        TypeOrmModule.forRoot(configService.getTypeOrmConfig()),
        // add other modules here as well
    ]
})
export class AppModule {}
_

write type-orm-config.ts

これは、CLI操作に役立つormconfig.jsonファイルを生成する簡単なスクリプトです。

_import configService from '../src/config.service';
import fs = require('fs');

fs.writeFileSync(
  'ormconfig.json',
  JSON.stringify(configService.getTypeOrmConfig(), null, 2), // last parameter can be changed based on how you want the file indented
);

_

プロジェクト構造

おそらくあなた自身のファイル構造とあなたのエンティティの名前の名前に基づいて、あなたのentitiesおよびmigrationsプロパティの正確な結合ステートメントを変更したいと思うでしょう。

私のプロジェクト構造は次のとおりです。

_.env // ALL environmental variables are stored here, both for Node and for other processes such as Docker
src
   | config.service.ts
   | app.module.ts // calls configService.getTypeOrmConfig()
   | main.ts
scripts // for CLI only operations
   | seed.ts // calls configService.getTypeOrmConfig() when creating a ConnectionOptions object for the database
   | write-type-orm-config.ts // calls configService.getTypeOrmConfig() to create an ormconfig.json file at the root, which I use for some NPM scripts
migrations
   | DB migrations go here...
_

Sample package.jsonスクリプト

これは、ormconfig.jsonファイルを必要とする場所があります。

_  "scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:dev:db:seed": "ts-node -r tsconfig-paths/register scripts/seed.ts",
    "start:debug": "nest start --debug --watch",
    "start:dev:autoconfig": "yarn run typeorm:migration:run && yarn run start:dev:db:seed",
    "start:prod": "node dist/src/main",
    "pretypeorm": "(rm ormconfig.json || :) && ts-node -r tsconfig-paths/register scripts/write-type-orm-config.ts",
    "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
    "typeorm:migration:generate": "yarn run typeorm -- migration:generate -n",
    "typeorm:migration:run": "yarn run typeorm -- migration:run"
  },
_

移行を生成するときに移行名を指定する必要があることに注意してください。_yarn run typeorm:migration:generate ${MIGRATION_NAME}_

参考文献

https://medium.com/better-programming/typeorm-miriatations-explained-FDB4F27CB1B3 ( - === - )
[。] https://github.com/gausim/nestjs-typeorm (上記のgitリポジトリ)

2
FinallyStatic

私はあなたのコードに問題が見つからなかったでしょう。

    1. .env
APP_PORT=
TYPEORM_CONNECTION = <mysql | mongodb | pg>
TYPEORM_Host = 
TYPEORM_USERNAME = 
TYPEORM_PASSWORD = 
TYPEORM_DATABASE = 
TYPEORM_PORT = 
TYPEORM_SYNCHRONIZE = <true | false>
TYPEORM_LOGGING = <true | false>

TYPEORM_ENTITIES=**/*.entities.ts,src/**/*.entities.ts,src/**/*.entity.ts
TYPEORM_MIGRATIONS=database/migration/*.ts
TYPEORM_MIGRATIONS_DIR=database/migration

 _
    1. config.service.ts _
import {TypeOrmModuleOptions} from '@nestjs/typeorm';
// tslint:disable-next-line: no-var-requires
require('dotenv').config();

class ConfigService {
    constructor(private env: {[key: string]: string | undefined}) {}

    private getValue(key: string, throwOnMissing = true): string {
        const value = this.env[key];
        if (!value && throwOnMissing) {
            throw new Error(`config error - missing env.${key}`);
        }

        return value;
    }

    public ensureValues(keys: string[]) {
        keys.forEach(key => this.getValue(key, true));
        return this;
    }
    public getTypeOrmConfig(): TypeOrmModuleOptions {
        return {
            type: 'mysql',
            keepConnectionAlive: true,
            Host: process.env.TYPEORM_Host,
            port: parseInt(process.env.TYPEORM_PORT) || 3306,
            database: process.env.TYPEORM_DATABASE,
            username: process.env.TYPEORM_USERNAME,
            password: process.env.TYPEORM_PASSWORD,
            entities: [__dirname + '/../**/*.entities{.ts,.js}']
        };
    }
}

const configService = new ConfigService(process.env).ensureValues([
    'TYPEORM_DATABASE',
    'TYPEORM_USERNAME',
    'TYPEORM_PASSWORD'
]);

export {configService};

 _
    1. app.module.ts _
@Module({
    imports: [
        TypeOrmModule.forRoot(configService.getTypeOrmConfig()),
    ]
})
export class AppModule {}
 _

この解決策が働いているかどうかを教えてください。

0
Riajul Islam

最終的な答えの改善(実際にはNESTJS Config Docsを使用するだけではありません)。私はこれがこのように掃除機であると思います。

db-config.ts

import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { registerAs } from "@nestjs/config";
import { config as setConfig } from 'dotenv';

setConfig();
setConfig({ path: '.dev.env' }); // use this if you use another .env file. Take the two setConfig if you use .env + other.env

export default registerAs('typeOrmConfig', (): TypeOrmModuleOptions => ({
    type: 'mysql',
    Host: process.env.MYSQL_Host || 'localhost',
    port: Number(process.env.MYSQL_PORT) || 3306,
    username: process.env.MYSQL_USER || 'test',
    password: process.env.MYSQL_PASSWORD || 'test',
    database: process.env.MYSQL_DATABASE || 'test',
    entities: ['dist/**/*.entity{.ts,.js}'],
    charset: "utf8mb4_unicode_ci",
    synchronize: false,
    cli: {
        migrationsDir: "src/migrations"
    },
    migrations: ["dist/migrations/**/*.js"],
}));
 _

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import dbConfig from './config/db-config';

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: '.dev.env',
      load: [dbConfig]
    }),
    TypeOrmModule.forRoot(dbConfig()),
    // etc...
  ],
  // etc...
});
 _

write-type-orm-config.ts

import * as fs from 'fs';
import dbConfig from './config/db-config';

try {
    fs.unlinkSync('ormconfig.json');
}
catch { }
fs.writeFileSync(
    'ormconfig.json',
    JSON.stringify(dbConfig(), null, 4),
);
 _

package.json

最終的な答えとの1行の違いなので、TSファイルのunlinkと互換性のあるウィンドウもあります。

"pretypeorm": "ts-node -r tsconfig-paths/register src/write-type-orm-config.ts",
 _

構造

|-- src/
| |-- config/
| | |-- db-config.ts
| |
| |-- migrations/
| | |-- *migration files*
| |
| |-- app.module.ts
| |-- write-type-orm-config.ts
|
|-- .env
|-- ormconfig.json
 _
0
Ripper346