4

经过几个小时的挖掘,我需要你的帮助!

上下文

我目前正在使用堆栈创建(早期阶段)一个应用程序:Nx(monorepo) + NestJS + TypeOrm

这是我的 ormconfig 文件:

    "type": "postgres",
    "host": "localhost",
    "port": 5432,
    "username": "***",
    "password": "****",
    "database": "****",
    "synchronize": false,
    "logging":false,
    "entities": ["apps/api/src/app/**/**.entity.ts"],
    "migrations":["apps/api/src/migration/**.ts"],
    "cli":{        
        "migrationsDir":["apps/api/src/migration"],
        "entitiesDir":["apps/api/src/app/**/**.entity.ts"]
    }
  }

这是我的迁移文件:

import {MigrationInterface, QueryRunner, Table} from "typeorm";

export class users1573343025001 implements MigrationInterface {
    public async up (queryRunner: QueryRunner): Promise<any> {
        await queryRunner.createTable(new Table({
          name: 'users',
          columns: [
            { name: 'id', type: 'bigint', isPrimary: true,
 isGenerated: true, generationStrategy: 'increment', unsigned: true },
            { name: 'username', type: 'varchar', isNullable: false },
            { name: 'password', type: 'varchar', isNullable: true },
          ]
        }))
      }

      public async down (queryRunner: QueryRunner): Promise<any> {
        await queryRunner.dropTable('users')
      }

}

问题

当我运行命令ng serve api来运行我的后端时,我遇到了这个问题:

语法错误:意外的令牌 {...

错误来自我的迁移文件:apps\api\src\migration\1573343025001-users.ts:1

有什么困扰我

如果我使用 typeorm 命令运行迁移,typeorm 可以毫无问题地运行它。 迁移users1573343025001已成功执行! 所以我不明白为什么迁移文件在迁移期间但在运行期间看起来对我的应用程序是正确的。

我已经尝试过的

  • 围绕这个主题的很多答案是:将迁移目录更改为 dist/migration。但我只是想为应用程序提供服务,而不是构建它。
  • 使用 typeorm 命令重新创建文件
  • 验证我的 package.json 是否有行 : "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js",to perform in typescript
  • npm install(谁知道?)
  • 删除迁移并运行命令ng serve api,应用程序启动时没有任何错误迹象

我可能错过了这些对我来说是新的技术的基本东西。希望所有这些都足够清楚,以便您了解情况。

谢谢你,

塞布

4

1 回答 1

4

OP可能找到了解决方案。这适用于遇到此问题的其他人。

查看https://github.com/nestjs/typeorm/issues/150#issuecomment-510716686和下一条评论以获取问题的解决方案。

您无法.ts在转译过程后加载文件。根据环境更改传递给实体的路径或将实体直接放入此数组。

我不想在每次创建新实体时都更新实体数组,所以我选择更新文件 glob 模式。

解决方案的关键:

/* replaced original two lines */
      // entities: ['**/*.entity{.ts,.js}'],
      // migrations: ['src/migration/*.ts'],

/* with these two lines */
      entities: [path.join(__dirname, '../**/*.entity{.ts,.js}')],
      migrations: [path.join(__dirname, '../migration/*{.ts,.js')],

现在npm run start:dev还是npm run start:debug不抛出错误。

这是完整的configuration.tsapp.module.ts

// src/config/configuration.ts

import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import path = require('path');

export default () => {
  const customConfigService = new CustomConfigService(
    process.env,
  ).ensureValues([
    'DATABASE_HOST',
    'DATABASE_PORT',
    'DATABASE_USER',
    'DATABASE_PASSWORD',
    'DATABASE_NAME',
  ]);
  return {
    port: parseInt(process.env.PORT, 10) || 4000,
    database: {
      host: process.env.DATABASE_HOST,
      port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
    },
    typeOrmModuleOptions: customConfigService.getTypeOrmConfig(),
  };
};

// see https://medium.com/@gausmann.simon/nestjs-typeorm-and-postgresql-full-example-development-and-project-setup-working-with-database-c1a2b1b11b8f
class CustomConfigService {
  constructor(private env: { [k: string]: string | undefined }) {}

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

  public ensureValues(keys: string[]) {
    keys.forEach(k => this.getValue(k, true));
    return this;
  }

  public getPort() {
    return this.getValue('PORT', true);
  }

  public isProduction() {
    const mode = this.getValue('NODE_ENV', false);
    return mode === 'production';
  }

  public getTypeOrmConfig(): TypeOrmModuleOptions {
    return {
      type: 'postgres',

      host: this.getValue('DATABASE_HOST'),
      port: parseInt(this.getValue('DATABASE_PORT')),
      username: this.getValue('DATABASE_USER'),
      password: this.getValue('DATABASE_PASSWORD'),
      database: this.getValue('DATABASE_NAME'),

      /* replaced original two lines */
      // entities: ['**/*.entity{.ts,.js}'],
      // migrations: ['src/migration/*.ts'],

      /* with these two lines */
      entities: [path.join(__dirname, '../**/*.entity{.ts,.js}')],
      migrations: [path.join(__dirname, '../migration/*{.ts,.js')],

      migrationsTableName: 'migration',

      cli: {
        migrationsDir: 'src/migration',
      },

      ssl: this.isProduction(),
    };
  }
}
// src/app.module.ts

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

// local imports
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ItemsModule } from './items/items.module';
import configuration from './config/configuration';

const envFilePath =
  process.env.NODE_ENV === 'production'
    ? 'envs/.production.env'
    : 'envs/.development.env';
@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath,
      load: [configuration],
    }),
    // see https://stackoverflow.com/questions/53426486/best-practice-to-use-config-service-in-nestjs-module
    // see https://github.com/nestjs/nest/issues/530#issuecomment-415690676
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => {
        const typeOrmModuleOptions = configService.get<TypeOrmModuleOptions>(
          'typeOrmModuleOptions',
        );
        console.log(`typeOrmModuleOptions= `, typeOrmModuleOptions);
        return typeOrmModuleOptions;
      },
      inject: [ConfigService],
    }),
    ItemsModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
于 2020-02-13T19:24:35.687 回答