nest: Typeorm fails to work in HMR

Cannot connect to database when using hmr


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

After creating a new project, I directly use the document example to connect to mongodb. If I use npm run start, it works. if I use npm run webpack && npm run start:hmr, it will fail, as shown below image

// ormconfig.json
{
  "type": "mongodb",
  "host": "localhost",
  "database": "polaris",
  "entities": [
    "src/**/**.entity.ts"
  ],
  "synchronize": true
}

Environment


Nest version: 5.0.0

 
For Tooling issues:
- Node version: 8.11.1  
- Platform:  Windows 10

Others:

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (2 by maintainers)

Commits related to this issue

Most upvoted comments

How do you go about registering the entities then? given that a glob import doesn’t work?

Yes, glob pattern will not work. You must provide class references to entities field instead.

import { Cat } from '../cat/cat.entity';

@Module({
    imports: [
        // provides: typeorm/Connection, typeorm/EntityManager
        TypeOrmModule.forRoot({
            entities: [
                Cat,
            ],
        }),
    ],
})
export class DatabaseModule { }

But fortunately webpack has feature require.context which allow to collect needed files.

// entity.context.ts (in root src folder)
export const entityContext =  require.context('.', true, /\.entity\.ts$/);
// database.module.ts
import { entityContext } from '../entity.context';

@Module({
    imports: [
        TypeOrmModule.forRoot({
            entities: [
                ...entityContext.keys().map(id => {
                    const entityModule = entityContext(id);
                    // We must get entity from module (commonjs)
                    // Get first exported value from module (which should be entity class)
                    const [entity] = Object.values(entityModule);
                    return entity;
                })
            ],
        }),
    ],
})
export class DatabaseModule { }

Putting my two cents here for people with the same problem.

The reason this is happening is because

  1. webpack bundles all the code into a separate bundle file, and in order for HMR to work, this bundle files is loaded and run as the server
  2. specifying entities: [__dirname + '/**/*.ts'] (or .js in my case) would cause typeorm to require those files (while the real entities is already loaded in the webpack bundle).
  3. when typeorm tries to get repository, it compares the entity passed-in to getRepository (for example, getRepository(User), where this User is loaded from the webpack bundle), with the ones loaded in the entities config, which is loaded from js/ts files.
  4. Even though they have the same name, they’re two different class (functions) loaded from different modules, so typeorm will not be able to find the correct one.

My workaround is based on the fact that all the modules are loaded, hence all the entities should be loaded already, via imports. This is especially true in NestJS, with the well-structured project. Specifically, for each module, either the module itself or the controller will import the entity somewhere.

By leveraging the internal mechanism of @Entity decorator, we’ll be able to get a list of entity classes.

Not sure how true this is, but seems to work well with both dev and prod settings.

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

// The modules are loaded from here, hence importing the entities
import { AModule } from './a/a.module';
import { BModule } from './b/b.module';

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

It is not problem of nest. It is about webpack, if you want to use glob pattern, use ts-node-dev