Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entities no longer registered after Hot Reloading #5676

Open
nealdyrkacz opened this issue Mar 13, 2020 · 69 comments
Open

Entities no longer registered after Hot Reloading #5676

nealdyrkacz opened this issue Mar 13, 2020 · 69 comments
Assignees

Comments

@nealdyrkacz
Copy link

nealdyrkacz commented Mar 13, 2020

Note from @imnotjames

I've edited the title. The most common issue I've seen in this thread relates to the hot-reload feature used by development environments & serverless environments.

What happens if that the registered entities are changed by the hot reload with new entities that are no longer registered even if they're similar.

If you believe your issue is unrelated to that, please open a new issue with details including a reproducible example.


Issue type:

[ ] question
[x ] bug report
[ ] feature request
[x ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[x ] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[ ] latest
[ ] @next
[x ] 0.2.24

Been using v0.2.22 and my ormconfig.js is as follows

module.exports = {
  type: 'postgres',
  host: process.env.TYPEORM_HOST,
  port: process.env.TYPEORM_PORT,
  username: process.env.TYPEORM_USERNAME,
  password: process.env.TYPEORM_PASSWORD,
  database: process.env.TYPEORM_DATABASE,
  synchronize: false,
  logging: false,
  entities: ['build/entity/*.js'],
  migrations: ['build/migration/*.js'],
  subscribers: ['build/subscriber/*.js'],
  cli: {
    entitiesDir: 'src/entity',
    migrationsDir: 'src/migration',
    subscribersDir: 'src/subscriber',
  },
};

which works fine, but I updated to v0.2.24 and I'm getting
"No repository for "" was found. Looks like this entity is not registered in current "default" connection?" when doing a simple query

        .getRepository(<Entity>)
        .find({ where: { active: true } });

Is there a documentation I am missing?

@nealdyrkacz nealdyrkacz changed the title v0.2.24 not registering entities as v0.2.22 v0.2.24 not registering entities through ormconfig.js as v0.2.22 Mar 13, 2020
@YoonSeongKyeong
Copy link

I also found this bug with mysql & typeorm v0.2.24 arising with getRepository(<Entity>)
everything was fine with typeorm v0.2.22

following is Error Message

RepositoryNotFoundError: No repository for "<Entity>" was found. Looks like this entity is not registered in current "default" connection?

@YoonSeongKyeong
Copy link

YoonSeongKyeong commented Mar 14, 2020

Debugger Behavior in RepositoryNotFoundError

this is v0.2.24 behavior with getRepository(<Entity>)

image
None of above condition fall in to true

this is v0.2.22 behavior with getRepository(<Entity>)

image
They fall to first condition and return true

this is watch of comparing targets: metadata.target , target, metadata.target === target

image

I think the removal of first condition is the source of this bug.

to make it have a right behavior

update of protected findMetadata(target: Function|EntitySchema<any>|string): EntityMetadata|undefined is necessary

I just find the source of this bug. #5486

@seandearnaley
Copy link

I'm also experiencing this, someone made a PR to fix but it's disputed: #5627

@seandearnaley
Copy link

also see this thread: #5592, not resolved yet

@YoonSeongKyeong
Copy link

YoonSeongKyeong commented Mar 17, 2020

I have solution @seandearnaley
we can solve this problem with replacing code like this

Before

protected findMetadata(target: Function|EntitySchema<any>|string): EntityMetadata|undefined {
        return this.entityMetadatas.find(metadata => {
            if (metadata.target === target)
                return true;
            if (target instanceof EntitySchema) {
                return metadata.name === target.options.name;
            }
            if (typeof target === "string") {
                if (target.indexOf(".") !== -1) {
                    return metadata.tablePath === target;
                } else {
                    return metadata.name === target || metadata.tableName === target;
                }
            }

            return false;
        });
    }

After

protected findMetadata(target: Function|EntitySchema<any>|string): EntityMetadata|undefined {
        return this.entityMetadatas.find(metadata => {
            if (Object.getPrototypeOf(metadata.target) === Object.getPrototypeOf(target)&&metadata.target.name===target.name)
                // First Check if target is made from same entity constructor as metadata target have. Then check if target name is same as metadata target name
                return true;
            if (target instanceof EntitySchema) {// I don't know if this condition is necessary
                return metadata.name === target.options.name;
            }
            if (typeof target === "string") {// I don't know if this condition is necessary
                if (target.indexOf(".") !== -1) {
                    return metadata.tablePath === target;
                } else {
                    return metadata.name === target || metadata.tableName === target;
                }
            }

            return false;
        });
    }

Object.getPrototypeOf(metadata.target) === Object.getPrototypeOf(target) will satisfy the problem requirement

  1. check if metadata.target is same as target
  2. check if target is not only same named function but also created by entity constructor (resolve false positivity)

@YoonSeongKyeong
Copy link

unfortunately I don't have test environment yet. I want anybody who already set up environment pull request from this revision

@seandearnaley
Copy link

@YoonSeongKyeong , I left a note on the PR but nobody from the TypeORM team are responding yet... this issue looks the same as the issue I posted 3 weeks ago: #5592

@seandearnaley
Copy link

also this issue looks the same also: #5587

@railsstudent
Copy link

I encountered the same issue with postgres + nestjs and spent few hours to debug Connection.js
I will downgrade to 0.2.22 until this is resolved.
Thanks.

@tuhlmann
Copy link

Same for me (NestJS 7 server). I tried to mitigate the issue by loading the entities directly, not via a glob pattern string, but setting up an array of all the entities. But the issues remains.

I'm willing to change my code accordingly if I knew what in my source is causing the issue. For now only sticking to the previous version is the only workaround I found.

@robertgr991
Copy link

Don't know if it's the same problem but I also encountered this and the problem in my case was that I had an ormconfig.js file but also a .env file with:

TYPEORM_CONNECTION = mysql
TYPEORM_HOST = localhost
TYPEORM_USERNAME = test
TYPEORM_PASSWORD = test
TYPEORM_DATABASE = test
TYPEORM_PORT = 3306

And because of this, TypeORM was loading the configuration from .env and completely ignored .ormconfig.js. I solved this by renaming my env vars.

@jmann-tw
Copy link

Is there any update on this issue?

@seandearnaley
Copy link

likewise interested in any updates, been months since an update.... wondering if there are factors preventing progress. I don't mind switching solutions.

@tuhlmann
Copy link

I'm also stuck with the previous version, would love to see an update here!

@pleerock
Copy link
Member

Can you guys specify if are you using serverless and who is not using serverless? As I understand only serverless users might have this problem.

@seandearnaley
Copy link

@pleerock , I am not using serverless, I think I commented on this here or another one of the related threads, I have a starter kit using 0.2.22, it runs on my node desktop, but parts of my app specifically unit tests fail with 0.2.24, no serverless involved https://github.com/seandearnaley/brainstrike-typescript-starter

@tuhlmann
Copy link

@pleerock I'm also not using serverless. I'm running a Nest 7 / NodeJS server.
I'm fine with changing my code, but I can't find what goes wrong and why the new version doesn't find my entity files any more. If it helps, this is my build & run command from package.json. From the built JS structure I read the entities (not from the source TS):

"dev:server": "tsc-watch -p tsconfig.server.json --onSuccess \"cross-env TZ=utc TS_NODE_PROJECT=tsconfig.server.json node -r tsconfig-paths/register -r ts-node/register/transpile-only dist-srv/server/main.js\""

I get the feel that maybe the paths I define in tsconfig.json mess with the entity detection somehow, but that's just a feeling...

@davecarlson
Copy link

i am not using serverless, and have this issue aswell. last working version, as others, is 0.2.22

@jmann-tw
Copy link

jmann-tw commented Jun 1, 2020

Not server-less here as well. Using node 12.16.1

@tuhlmann
Copy link

tuhlmann commented Jun 6, 2020

@pleerock @millsmcilroy Is there any update on this issue?

Would a (kind of) minimal example help you find a solution- or tell me how I can persuade typeorm to accept my entities?

I took the better half of the day to strip down my NestJS application to a small example app with just one entity.

I added 2 start scripts:

  • if I start the app with ts-node-dev and point entities to the /src/**/*.entity{.ts,.js} directory or to the Entity class, this works fine.
  • if I start by compiling to JS and then point node to the compiled JS or the Entity class, this fails.
    • if I point the entities property to the source ts files (not the compiled output) this also works, but it fails in the real app with multiple entities.

Not sure if this problem has to do with ts-config paths that I'm using in the scripts below.

Should the entities property point to the TS source files or the generated JS output, what is the recommended way?

Please let me know how I can help to resolve this issue.

Thank you!

--

This is the start script that works:

"dev:server2": "cross-env NODE_ENV=development ts-node-dev --type-check --no-notify --project tsconfig.server.json --respawn --transpileOnly --all-deps -r tsconfig-paths/register ./src/server/main.ts"

This is the more involved start script that fails:

"dev:server": "tsc-watch -p tsconfig.server.json --onSuccess \"cross-env TZ=utc TS_NODE_PROJECT=tsconfig.server.json node -r tsconfig-paths/register -r ts-node/register/transpile-only dist-srv/server/main.js\""

@tuhlmann
Copy link

tuhlmann commented Jun 7, 2020

Here's a small example repository that exhibits this problem: https://github.com/tuhlmann/typeorm-entity-not-found

@tuhlmann
Copy link

@pleerock Any news on this, any advice how to change the code to still be able to use the latest typeOrm?

@tuhlmann

This comment has been minimized.

@pleerock
Copy link
Member

pleerock commented Jul 7, 2020

There are no problems with using TypeORM without NestJS and I checked your repo @tuhlmann and I see the problem, but I'm sure it doesn't go from TypeORM. We had one change somewhere in 0.2.20 and it clearly didn't work well, we had to revert it in 0.2.24. So, code you have right now in 0.2.24 and above is the same what we had in 0.2.19 and earlier. Try to test your app with 0.2.19 and earlier. I guess it would not work.

I assume there was a change on NestJS side that somehow rely on the wrong change we had in 0.2.20 and 0.2.23.
This is something related to references, maybe something is being cloned, or some references are being replaced.

Adding @kamilmysliwiec maybe he can help

@Devimal
Copy link

Devimal commented Nov 14, 2020

I don't think this is an issue caused by changes on NestJS's side.

First time I hit this problem was back in March when upgrading TypeOrm to make use of the new soft-delete feature. I did not update NestJS back then nor since, but the app still broke when attempting to go above 0.2.22.

In my case it was pretty clear isolated breakage on TypeOrm's side starting from 0.2.23.

@wi-ski
Copy link
Contributor

wi-ski commented Nov 28, 2020

I have arrived here after many battles - happy to know this is being looked into.

@wi-ski
Copy link
Contributor

wi-ski commented Nov 28, 2020

Confirming downgrading to 0.2.22 worked for me.

@gbahamondezc
Copy link

gbahamondezc commented Jan 9, 2021

Same problem here, using typeorm along with serverless-offline (multiple functions running at same time) using any version > 0.2.22 version, started downgrading from v0.2.29.

Using 0.2.22 works just fine.

@krolow
Copy link

krolow commented Jan 27, 2021

I still facing this issue, have someone figure out a way to handle it?

@krolow
Copy link

krolow commented Feb 3, 2021

my problem was because I was using nextjs so looks like it was related to HMR...

Now when in development when getting the open connection I'm re-hydrating the entities/metada:

export async function getConnection(): Promise<Connection> {
  const currentConnection: Connection = await connection;

  if (currentConnection.isConnected) {
    // @ts-ignore
    currentConnection.options.entities = getEntities();

    // @ts-ignore
    currentConnection.buildMetadatas();

    if (currentConnection.options.synchronize) {
      await currentConnection.synchronize();
    }
  }

  return currentConnection;
}

``

@jedireza
Copy link

jedireza commented Feb 3, 2021

I'm also using Next.js and this has been such a big time killer. It works fine with production builds since HMR isn't a factor.

The above hack that krolow shared for rebuilding metadata doesn't work for me (v0.2.30). The only relief I can get is to monkey patch my local file node_modules/typeorm/connection/Connection.js with this change: https://github.com/typeorm/typeorm/pull/5627/files

@unframework
Copy link

Just to chime in for anyone else reading, this is the simplest helper function that I could think of to bust HMR issues with TypeORM entity classes in Next.js:

let connectionReadyPromise: Promise<void> | null = null;

function prepareConnection() {
  if (!connectionReadyPromise) {
    connectionReadyPromise = (async () => {
      // clean up old connection that references outdated hot-reload classes
      try {
        const staleConnection = getConnection();
        await staleConnection.close();
      } catch (error) {
        // no stale connection to clean up
      }

      // wait for new default connection
      await createConnection(); // usual connection options go here
    })();
  }

  return connectionReadyPromise;
}

Then in any server-side code I do await prepareConnection() before performing any DB-related calls (which is similar to what above commenters suggest as well). I put it in its own file for easy inclusion, like src/db.ts.

I can confirm that this works with NextAuth on my codebase; should also work with any other HMR-enabled server solution. What I like about this, aside from the small size, is that the DB connection is simply restarted without doing any "surgery" on the entities list, updating metadata, etc. Perfectly fine for development mode.

@jedireza
Copy link

I also have a shared async function that returns an established db connection (/server/db.ts). In that code I also tried to detect when HMR changed the identity of the entity as described above and never had any luck with it. Maybe that's because I chose an active record approach vs data mapper.

After reading why #5627 wasn't accepted, I still think this would be a good dev environment option TypeORM could provide.

@xorander00
Copy link

I'm seeing the same issue with Next.js and most likely HMR.

@wi-ski
Copy link
Contributor

wi-ski commented Mar 5, 2021

Do we still need an example repo? Happy to make one if we dont have one. This issue sucks - let's kill it

@malteneuss
Copy link

I found a workaround that may be worth trying (source):
In addition to listing the entities manually

createConnection({
  ...
  entities: [
    UserEntity,    //  <--- here
  ],
});

i then had to lookup the repository by name/string

connection.getRepository<UserEntity>("UserEntity")

instead of by type/class

connection.getRepository(UserEntity)

to avoid the problem that HMR seems to create different compiled versions of UserEntity as described here:

Because TypeORM connection manager is not aware of entity class reloads ...
... Next.js will load and create a class reference for it - let's call it "User v1". ... Now, once you edit that class file, Next.js will
perform a hot reload, and there are now two different class references living inside runtime memory. One is the original "User
v1" and another one is the freshly recompiled "User v2". Your route code is now using the "User v2" class reference, but the
connection still has "User v1" in its list of known entities. When you try to e.g. call getRepository(User) in your code, you will not > be passing the same class reference as what TypeORM "knows"

@jrnxf
Copy link

jrnxf commented Mar 14, 2021

I found a workaround that may be worth trying (source):
In addition to listing the entities manually

createConnection({
  ...
  entities: [
    UserEntity,    //  <--- here
  ],
});

i then had to lookup the repository by name/string

connection.getRepository<UserEntity>("UserEntity")

instead of by type/class

connection.getRepository(UserEntity)

to avoid the problem that HMR seems to create different compiled versions of UserEntity as described here:

Because TypeORM connection manager is not aware of entity class reloads ...
... Next.js will load and create a class reference for it - let's call it "User v1". ... Now, once you edit that class file, Next.js will
perform a hot reload, and there are now two different class references living inside runtime memory. One is the original "User
v1" and another one is the freshly recompiled "User v2". Your route code is now using the "User v2" class reference, but the
connection still has "User v1" in its list of known entities. When you try to e.g. call getRepository(User) in your code, you will not > be passing the same class reference as what TypeORM "knows"

You have just made a tired man at 2 in the morning EXTREMELY happy. This works perfectly!! When I deployed my app to Vercel in production mode I was still seeing the issue, even though there should be no HMR related-issues given that context. Switching my getRepository calls to this syntax worked wonders. What an exhausting issue. I really hope this gets resolved soon.

@wi-ski
Copy link
Contributor

wi-ski commented Mar 15, 2021

Wow @malteneuss 👏

cwikla added a commit to puzzlefin/typeorm that referenced this issue Apr 2, 2021
@rwieruch
Copy link

@malteneuss your fix made my day. Thank you!

@Devimal
Copy link

Devimal commented Jun 14, 2021

I could not use @malteneuss's solution due to code base specifics. I ended up monkey patching using @jedireza's link.

Here is the NestJS code in case it helps somebody:

// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Connection, EntityMetadata, EntitySchema } from 'typeorm';
import { AppController } from './app.controller';

// src: https://github.com/typeorm/typeorm/issues/5676#issuecomment-772652004
(Connection.prototype as any).findMetadata = function (
    target: any
) {
    return this.entityMetadatas.find((metadata: any) => {
        if (
            metadata.target === target ||
            (typeof metadata.target === 'function' &&
                typeof target === 'function' &&
                metadata.target.name === target.name)
        ) {
            return true;
        }

        if (target instanceof EntitySchema) {
            return metadata.name === target.options.name;
        }
        if (typeof target === 'string') {
            if (target.indexOf('.') !== -1) {
                return metadata.tablePath === target;
            } else {
                return metadata.name === target || metadata.tableName === target;
            }
        }

        return false;
    });
};

@Module({
    imports: [
        TypeOrmModule.forRoot(connection),
    ],
    controllers: [AppController],
    providers: [],
})
export class AppModule {}

@imnotjames
Copy link
Contributor

imnotjames commented Jun 19, 2021

If you'd like to monkey patch it you can. This will break in many, many cases. For example, changes to entities will not be reflected after a reload.

Your best bet, imo, is on any reload is to close the connection & open a new one. This is because we don't support re-building the entities.

@imnotjames imnotjames changed the title v0.2.24 not registering entities through ormconfig.js as v0.2.22 Entities no longer registered after Hot Reloading Jun 19, 2021
klingebiel added a commit to e-Learning-by-SSE/stu-mgmt-service that referenced this issue Aug 2, 2021
@LeOndaz
Copy link

LeOndaz commented Sep 3, 2021

I can confirm this still happens, happened with me in a serverless environment. I rolled back to v0.2.22 and it got fixed.

@LaCocoRoco
Copy link

The solution from malteneuss works with "getRepository", but i could not find a way to use this fix for "getCustomRepository". I did a rollback to v0.2.22 like LeOndaz mentioned it.

@jedireza
Copy link

I'm also using Next.js and this has been such a big time killer. It works fine with production builds since HMR isn't a factor.

The above hack that krolow shared for rebuilding metadata doesn't work for me (v0.2.30). The only relief I can get is to monkey patch my local file node_modules/typeorm/connection/Connection.js with this change: https://github.com/typeorm/typeorm/pull/5627/files

For posterity I wanted to drop a note about this. I revisited this today and it seems like I can skip the monkey patch by adapting the solution from: #6241 (comment)

In my prepareConnection()/ensureConnection() function, when I find an existing connection AND I'm in a non production environment I apply this little hack. *insert sigh of relief* 🤞

         if (connection?.isConnected) {
             console.log('DB existing connection found.');
+
+            if (process.env.NODE_ENV !== 'production') {
+                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+                // @ts-ignore
+                connection.options.entities = Entities;
+
+                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+                // @ts-ignore
+                connection.buildMetadatas();
+
+                if (connection.options.synchronize) {
+                    await connection.synchronize();
+                }
+            }
+
             return connection;
         }

@SimantoR
Copy link

I've been using 0.2.45 and the problem persists. I haven't tried the solution by @jedireza but I did downgrade to 0.2.22 and it's working but I now have severe package levels. What a sadness, Very tempted to diff between 0.2.22 and 0.2.23 and see what the hell happened.

@wi-ski
Copy link
Contributor

wi-ski commented Aug 29, 2022

Figured I would throw in this completely useless statement: Its Aug 2022, this issue has been in my life >1 year. It's clearly difficult to address.

Thank you TypeORM team for everything you.

@nk11dev
Copy link

nk11dev commented May 10, 2023

Hot Reload is must-have feature for development. Recently, I decided to try TypeORM in my pet app. There were some moments and bugs that I did not like, but in general a positive impression was formed.

But now, I came across with bug, almost the same as described in this issue.
TypeORM crashes after Hot Reload with "no metadata was found" error.

This problem is already three years old and no one fixes it despite its importance.

This issue was the last straw and I will try other ORMs to give them a chance.

@pleerock
Copy link
Member

Issue you have might be something specific to your setup. I do use hot reload in some of my projects and never had issues with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests