nx: tsconfig target incompatibility with NestJS and TypeORM module

Prerequisites

  • I am running the latest version
  • I checked the documentation and found no answer
  • I checked to make sure that this issue has not already been filed
  • I’m reporting the issue to the correct repository (not related to Angular, AngularCLI or any dependency)

Expected Behavior

Current Behavior

Given a tsconfig file with es6 target and the following typeorm config :

{
   ...
   entities: ['apps/api/**/**.entity.ts'],
   migrations: ['apps/api/migrations/*.ts'],
   migrationsRun: true
   ...
}

When I start the nestjs application I get the following error when typeorm tries to load the entities

[Nest] 21470   - 2019-05-25 10:18   [NestFactory] Starting Nest application...
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] AppModule dependencies initialized +129ms
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] CoreModule dependencies initialized +9ms
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] PubSubModule dependencies initialized +2ms
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] PassportModule dependencies initialized +7ms
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] JwtModule dependencies initialized +1ms
[Nest] 21470   - 2019-05-25 10:18   [InstanceLoader] GraphQLModule dependencies initialized +1ms
[Nest] 21470   - 2019-05-25 10:18   [TypeOrmModule] Unable to connect to the database. Retrying (1)... +152ms
/Users/jonjon/work/nx/typefaster/apps/api/src/app/features/content/content.entity.ts:1
(function (exports, require, module, __filename, __dirname) { import * as GQL from '@tf/apps/api/typings/graphql';
                                                                     ^

SyntaxError: Unexpected token *
    at new Script (vm.js:80:7)
    at createScript (vm.js:274:10)
    at Object.runInThisContext (vm.js:326:10)
    at Module._compile (internal/modules/cjs/loader.js:664:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)

Then I tried to use entity classes directly instead of putting the paths : entities: ['apps/api/**/**.entity.ts'] --> entities: [UserEntity, ContentEntity, RandomEntity, RecordEntity]

Now I get the following error but for typeorm migrations :

[Nest] 21543   - 2019-05-25 10:27   [NestFactory] Starting Nest application...
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] AppModule dependencies initialized +155ms
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] CoreModule dependencies initialized +1ms
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] PubSubModule dependencies initialized +1ms
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] PassportModule dependencies initialized +1ms
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] JwtModule dependencies initialized +16ms
[Nest] 21543   - 2019-05-25 10:27   [InstanceLoader] GraphQLModule dependencies initialized +1ms
[Nest] 21543   - 2019-05-25 10:27   [TypeOrmModule] Unable to connect to the database. Retrying (1)... +171ms
/Users/jonjon/work/nx/typefaster/apps/api/migrations/1551728841387-Migration.ts:1
(function (exports, require, module, __filename, __dirname) { import {MigrationInterface, QueryRunner} from "typeorm";
                                                                     ^

SyntaxError: Unexpected token {
    at new Script (vm.js:80:7)
    at createScript (vm.js:274:10)
    at Object.runInThisContext (vm.js:326:10)
    at Module._compile (internal/modules/cjs/loader.js:664:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)

Context

Please provide any relevant information about your setup:

  • version of Nx used : 7.8.6
  • version of Angular CLI used : 7.3.1
  • version of Angular DevKit used : 0.13.9
  • 3rd-party libraries and their versions “@nestjs/common”: “^6.2.4” “@nestjs/core”: “^6.2.4” “@nestjs/typeorm”: “^6.1.1”

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 29
  • Comments: 49 (14 by maintainers)

Most upvoted comments

This fixed it for me:

import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { getMetadataArgsStorage } from 'typeorm'; import { typeOrmConfig } from '../config/typeorm.config';

@Module({ imports: [ ..., TypeOrmModule.forRoot({ ...typeOrmConfig, entities: getMetadataArgsStorage().tables.map(tbl => tbl.target) }) ] }) export class AppModule {}

You can also “fix” this issue by instead of supplying globs to the entities array, you supply the entities directly.

entities: [User, Product]

However this would require extra steps to introduce an entity. (Can be eased with using an index.ts and combining exports)

I fixed this by changing my entities in my ormconfig.ts

from:

entities: ['apps/**/*.entity.ts']

to:

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

Make sure you have import { join } from 'path';

Up

My workaround works like this for run CLI command and connect entities from api code:

at the root folder I put a ormconfig.json for the CLI

{
   "type": "mysql",
   "host": "127.0.0.1",
   "port": 9906,
   "username": "root",
   "password": "root",
   "database": "test",
   "synchronize": true,
   "logging": false,
   "entities": [
      "libs/typeorm/src/lib/entity/**/*.ts"
   ],
   "migrations": [
      "libs/typeorm/src/lib/migration/**/*.ts"
   ],
   "subscribers": [
      "libs/typeorm/src/lib/subscriber/**/*.ts"
   ],
   "cli": {
      "entitiesDir": "libs/typeorm/src/lib/entity",
      "migrationsDir": "libs/typeorm/src/lib/migration",
      "subscribersDir": "libs/typeorm/src/lib/subscriber"
   }
}

app.module.ts

TypeOrmModule.forRoot({`
      "type": "mysql",
      "host": environment.db.host,
      "port": environment.db.port,
      "username": environment.db.username,
      "password": environment.db.password,
      "database": environment.db.database,
      "synchronize": true,
      "autoLoadEntities": true
    })

The really important here is you need to “overwrite” the values in app.module, if not the config from the ormconfig.json will not work. autoLoadEntities works like a charm here.

npm script to run typeorm CLI

"typeorm": "ts-node -P libs/typeorm/tsconfig.json ./node_modules/.bin/typeorm"

// ex: npm run typeorm migration:generate -- -n UserMigration

and finally the libs/typeorm/tsconfig.json

{
  "compilerOptions": {
    "types": ["node", "jest"],
     "lib": [
        "es5",
        "es6"
     ],
     "target": "es2015",
     "module": "commonjs",
     "moduleResolution": "node",
     "outDir": "./build",
     "emitDecoratorMetadata": true,
     "experimentalDecorators": true,
     "sourceMap": true
  },
  "include": ["**/*.ts"]
}

Works only with module commonjs

Hope this help.

I’m also experiencing this issue. Have there been any update on this?

This fixed it for me:

import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { getMetadataArgsStorage } from 'typeorm'; import { typeOrmConfig } from '../config/typeorm.config';

@Module({ imports: [ ..., TypeOrmModule.forRoot({ ...typeOrmConfig, entities: getMetadataArgsStorage().tables.map(tbl => tbl.target) }) ] }) export class AppModule {}

thank you very much, you save my days!

Talked to @FrozenPandaz about it. The issue is because we bundle the nest app.

We have a plan in mind. It should be addressed by Nx 9.

I think I found a solution to use TypeOrm Cli without the need to build the library

I used to have my DB entities and migrations as a buildable library and to use TypeOrm Cli I used to build and configure ormconfig.json to dist folder. another problem was that it didn’t let me import from other libraries.

With this solution, it works with TypeOrm CLI to generate the migration and it’s without the need for a buildable library and also Nestjs can run migrations too.

  1. installed tsconfig-paths: ^3.12.0 “typeorm”: “^0.2.41” “ts-node”: “~9.1.1”
npm I tsconfig-paths --save-dev
  1. my ormconfig.json looks like this: it’s in the root of monorepo (I have my libs in root, yours might be in libs directory)
{
    ...,
    "entities": [
        "./Lib.LoHi.Backend.Persistence/src/entities/*.entity.ts"
    ],
    "migrations": [
        "./Lib.LoHi.Backend.Persistence/src/migrations/*.ts"
    ],
    "cli": {
        "migrationsDir": "./Lib.LoHi.Backend.Persistence/src/migrations"
    },
    "ssl": false
}

  1. I created a tsconfig.typeorm.json in the root of the library next to other configs

Lib.LoHi.Backend.Persistence/tsconfig.typeorm.json and added ts-node part to support paths. (note that it’s extending base tsconfig so it can read paths)

{
    "extends": "../tsconfig.base.json",
    "compilerOptions": {
        "module": "commonjs",
        "outDir": "../_dist/out-tsc",
        "declaration": false,
        "sourceMap": false,
        "types": ["node"],
        "target": "es6"
    },
    "exclude": ["**/*.spec.ts", "**/*.test.ts"],
    "include": ["**/*.ts"],
    "ts-node": {
        "require": ["tsconfig-paths/register"]
    }
}

  1. added these scripts to package.json
{
    "scripts": {
        "db:migration:create": "npm run typeorm -- migration:create -n empty",
        "db:migration:run": "npm run typeorm -- migration:run",
        "db:migration:show": "npm run typeorm -- migration:show",
        "db:migration:revert": "npm run typeorm -- migration:revert",
        "generate:db:migration": "npm run typeorm -- migration:generate -n size-to-integer --pretty",
        "typeorm": "ts-node --project ./Lib.LoHi.Backend.Persistence/tsconfig.typeorm.json ./node_modules/typeorm/cli.js"
    }
}

this was for TypeOrm CLI to work with paths and typescript files.

the NestJs part is easy.

create two arrays, one for migrations classes and one for entities, and use them in forRoot config. I think these fields are possible with path (I didn’t test and don’t recommend, you can create a script to go through your entities and migrations and create the exports file.)

export const migrations = [
 ...
export const entities = [
 ...
];
import { migrations, entities } from '@lohi/be-persistence';

@Module({
    imports: [TypeOrmModule.forRoot({ ...config, entities, migrations, keepConnectionAlive: true })],
    controllers: [],
    providers: [DbConfigurationService, DbService],
    exports: [TypeOrmModule]
})
export class DbModule implements OnModuleInit {}

Dear everyone in this thread,

i managed to solve the migrations issue in my nrwl/nx application. This is, what i did:

  1. (optional!) install scripty (https://github.com/testdouble/scripty), which lets you execute shell-scripts from npm; npm install --save-dev scripty. While this step is optional, it makes your life easier

  2. add a few npm scripts to your package.json file to easily trigger the commands:

{
  // ...
  scripts : {
    // ...
    "migrate:create": "scripty",
    "migrate:run": "scripty"
  }
}

Note that those npm scripts just call the previous mentioned scripty library.

  1. Create the proper scripty shell scripts: in your root folder (next to the package.json file) create a scripts folder. This folder is “crawled” by scripty when invoking a npm script. For example, if your command is called migrate:create (see step 2), you need to create a file in scripts/migrate/create.sh.

scripts/migrate/create.sh

#!/usr/bin/env sh

cd ./apps/"$1"/src
npx typeorm migration:create -n "$2"

scripts/migrate/run.sh

#!/usr/bin/env sh

cd ./apps/"$1"/src
npx tsc database/migrations/src/*.ts --outDir database/migrations/generated
npx typeorm migration:run
  1. add an ormconfig.js file into the root directory of your application that works with a database. As your mono-repository may contain multiple apps that interact with databases, you may want to have different ormconfig.js files.

apps/api/src/ormconfig.js

module.exports = {
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  username: 'databaseuser',
  password: 'databasepassword',
  database: 'databasename',
  migrationsTableName: 'migrations',
  migrations: ['database/migrations/generated/*.js'],
  cli: {
    migrationsDir: 'database/migrations/src',
  },
  synchronize: false,
};
  1. To create a new migration, call
# npm run migrate:create PROJECTNAME MIGRATIONNAME
npm run migrate:create api CreateUsersTable

This will create a new file apps/api/src/database/migrations/src/123456789-CreateUsersTable.ts. You can now fill this migration with life!

  1. To run (i.e., execute) all migrations and replicate them to the database, you can call
# npm run migrate:run PROJECTNAME
npm run migrate:run api

This command will first transpile all existing apps/api/src/database/migrations/src/*.ts into javascript and store them in apps/api/src/database/migrations/generated. The latter are then executed by typeorm.

I hope this solution works for you guys!

This fixed it for me:

import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { getMetadataArgsStorage } from 'typeorm'; import { typeOrmConfig } from '../config/typeorm.config';

@Module({ imports: [ ..., TypeOrmModule.forRoot({ ...typeOrmConfig, entities: getMetadataArgsStorage().tables.map(tbl => tbl.target) }) ] }) export class AppModule {}

Yes Oh thank God Yes

Hi folks! I’ve reclassified this issue as an enhancement as the solution we’re going to pursue is creating a builder for running ts-node.

The reason that the repro provided by @beeman (thanks!) doesn’t work is that the node process can’t read ts files directly. Providing a ts-node builder should mean that this approach will work in future.

If you can’t wait for the builder implementation, I recommend that, for the moment, you try one of the workarounds which involve importing your entity files so that they don’t get referenced as uncompiled typescript.

Any updates on this issue?

I found that inside generated dist/main.js file don’t have any typeorm migration code. How to load migrations file after build?

Should I tsc migration files and copy into dist folder?

@kuccilim Im having same issue, did You fix it?

According to the docs https://docs.nestjs.com/techniques/database all you enter is entities: [__dirname + '/**/*.entity{.ts,.js}'],

Which gives an error noting that it cannot find any of your entity.ts files No repository for "Post" was found. Looks like this entity is not registered in current "default" connection?

Dumping __dirname reveals it is running in the dist/app/xyz folder, which means you would have to use entities: [__dirname + '/../../../app/xyz/src/app/**/*.entity{.ts,.js}'],

But I can confirm that @NailRode suggestion of entities: getMetadataArgsStorage().tables.map(tbl => tbl.target) works for me.

I found that inside generated dist/main.js file don’t have any typeorm migration code. How to load migrations file after build?

Should I tsc migration files and copy into dist folder?