nestjs - 使用一种配置为 CLI 和 NestJS 应用程序配置 TypeORM

标签 nestjs typeorm

我在 NestJS 应用程序中使用 TypeORM。我的 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.js 中复制凭据在我的 config.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

由于环境变量现在定义如下:TypeORM Documentation ,我继续重构app.module.ts如下所示:
@Module({
  imports: [TypeOrmModule.forRoot(), ConfigModule],
  controllers: [],
  providers: [],
})
export class AppModule {}

现在我得到 env vars DATABASE_HOST 的错误, DATABASE_PORT当我使用 typeorm 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_环境变量在这里互斥?我们真的需要将环境变量复制到他们的 DATABASE_ 中吗?为了让 TypeORM 在 CLI 和 NestJS 应用程序的上下文中工作?这似乎非常错误。让 TypeORM 在 CLI(我希望它用于开发中的迁移)和 NestJS 应用程序中同时工作而不必复制这些变量的正确方法是什么?

最佳答案

解决方案

此解决方案允许您对 CLI 使用和应用程序使用使用相同的参数,而不会遇到代码重复。

使用 path.join():

配置.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项目根目录下的文件。您也可以为它提供 TypeOrmModuleOptions 参数,我会推荐它。我建议完全按照 Riajul Islam 和 Muhammad Zeeshan 的做法去做:

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

写类型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...

示例 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-migrations-explained-fdb4f27cb1b3 (关于用 NestJS 配置 TypeORM 环境的好文章)
https://github.com/GauSim/nestjs-typeorm (上面的 Git 存储库)

关于nestjs - 使用一种配置为 CLI 和 NestJS 应用程序配置 TypeORM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59913475/

相关文章:

node.js - 如何在nest js中实现guard的集成测试?

javascript - Typeorm - 多个 where 语句

mysql - 如何在带有typeORM的nestjs中创建日期类型和日期时间类型的列?

javascript - 在 NestJS 应用程序中注入(inject)模拟以进行契约(Contract)测试

javascript - 使用自定义端点扩展现有 API

typeorm - 强制 TypeORM Entity.create 创建一个对象实例

node.js - 查询失败错误 : the column "price" contain null values - TypeORM - PostgreSQL

javascript - 如何在 Nest.js 中为 @Body 构造 DTO

node.js - ffmpeg 说该文件不存在,但它确实存在

node.js - 如何生成使用 NESTJS 完成的 API 的生产版本