Nakama-dev
Nakama-dev

Reputation: 41

Nx-NestJS-TypeOrm: SyntaxError: Unexpected token {

After hours of digging, I need your help !

The context

I'm currently creating(early stage) an application with the stack : Nx(monorepo) + NestJS + TypeOrm

Here is my ormconfig file :

    "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"]
    }
  }

Here is my migration file :

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')
      }

}

The problem

When I run the command ng serve api to run my backend, I face this issue :

SyntaxError: Unexpected token {...

The error comes from my migration file : apps\api\src\migration\1573343025001-users.ts:1

What bugs me

If I run my migration with typeorm command, typeorm is able to run it without any trouble. Migration users1573343025001 has been executed successfully! So I don't understand why the migration file looks correct to my app during the migration but during the run.

What I have already tried

I'm probably missing a basic thing with this technologies that are new to me. Hope all this is clear enough for you to understand the situation.

Thank You,

Séb

Upvotes: 4

Views: 5004

Answers (1)

An Vad
An Vad

Reputation: 428

Likely the OP has found a solution. This is for others who came across this issue.

Look at https://github.com/nestjs/typeorm/issues/150#issuecomment-510716686 and the next comment for a solution to the problem.

You can't load .ts files after transpilation process. Either change path passed to entities based on the environment or put entities directly to this array.

I did not want to have to update the entities array each time a new entity is created, so I chose to update the file glob pattern.

The meat of the solution:

/* 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')],

Now npm run start:dev or npm run start:debug does not throw errors.

Here's the full configuration.ts and app.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 {}

Upvotes: 4

Related Questions