1

When using TypeORM with Next.js, server-side hot-reloading is used by default and is convenient, except for one thing: Every time a hot reload occurs, the code is re-run which results in making rebuilding the entity classes so they are not the "same" as the old versions which are remembered by TypeORM (they don't have referential equality). This leads to EntityMetadataNotFound errors when TypeORM compares the new User entity to the old User entity and finds that they do not match. You can see more on this issue here: EntityMetadataNotFound: No metadata for "BusinessApplication" was found. To fix this I decided to make a new connection on each hot reload like this:

/**
 * Database manager class
 */
class Database {
  private connectionManager: ConnectionManager;

  /**
   * Keeps track of whether this Database instance remembers making a new connection. If it doesn't,
   * and an existing connection was found anyway, that means that the module was hot-reloaded.
   */
  private hasCreatedConnection = false;

  constructor() {
    this.connectionManager = getConnectionManager();
  }

  private async getConnection(): Promise<Connection> {
    const DEFAULT_CONNECTION_NAME = 'default';
    const currentConnection = this.connectionManager.has(DEFAULT_CONNECTION_NAME)
      ? this.connectionManager.get(DEFAULT_CONNECTION_NAME)
      : undefined;
    // if connection exists but we don't remember creating it, it's because of hot reloading
    // and that means a new connection needs to be created, or else entity metadata won't match
    // from the old session.
    // https://stackoverflow.com/questions/60677582/entitymetadatanotfound-no-metadata-for-businessapplication-was-found
    if (currentConnection && !this.hasCreatedConnection) {
      console.debug('recreating connection due to hot reloading');
      if (currentConnection.isConnected) {
        await currentConnection.close();
      }
      console.debug('done closing, making new connection..');
      return this.createConnectionWithName(DEFAULT_CONNECTION_NAME);
    }
    if (currentConnection) {
      if (!currentConnection.isConnected) {
        return currentConnection.connect();
      } else return currentConnection;
    } else {
      return this.createConnectionWithName(DEFAULT_CONNECTION_NAME);
    }
  }

  private createConnectionWithName(name: string): Promise<Connection> {
    this.hasCreatedConnection = true;
    return createConnection({
      name,
      type: 'postgres',
      url: process.env.DATABASE_URL,
      synchronize: true,
      entities: [Business, Qualification, Customer, BaseOffer, ProductOffer, ServiceOffer]
    });
  }

  public getManager(): Promise<EntityManager> {
    return this.getConnection().then(conn => conn.manager);
  }
}

Unfortunately, making a connection to the database is incredibly slow (~10 seconds) (and this may be an issue of its own), so every API call now takes a very long time. How could this be fixed? I also opened an issue on the typeorm repository here: https://github.com/typeorm/typeorm/issues/5876

Robert Moore
  • 2,207
  • 4
  • 22
  • 41

0 Answers0