node.jstypeormesbuildtypescript-decorator

ESbuild with typescript, connecting typeorm entities to the dataSource


! UPD !
Running the actual Entity creation and DB init straight with ts-node also give that error, so i assume it's not the build problem, but actual ts + typeorm + ts-node issue

Introduction

The node js project uses esbuild with esbuildPluginTsc from "esbuild-plugin-tsc" package like so:

import * as esbuild from 'esbuild';

const config = {
    entryPoints: ['server.js'],
    bundle: true,
    outdir,
    platform: 'node',
    packages: 'external',
    external: ['uuid'],
    plugins: [
      esbuildPluginTsc({
        force: true
      }),
   ...], // other static files configs
  ...options,
}

await esbuild.build(config);

Also the tsconfig.json configured right, meaning it has enabled decorators flags:

Prerequisites

Simple typeORM entity:

import 'reflect-metadata';
import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
} from 'typeorm';

@Entity()
export class Common {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ type: 'varchar', nullable: false })
  name: string;
}

Trying to create a DataSource from typeORM:

import 'reflect-metadata';
import { Common } from '@entity/common-entity'; // my alias for entity
import { DataSource } from 'typeorm';

const myDataSource = new DataSource({
  type: 'postgres',
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT || '5432'),
  username: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  entities: [Common],
  logging: true,
  synchronize: true,
});

export const init = async () => {
  myDataSource
    .initialize()
    .then(async () => {
      logger.info('Data Source has been initialized!');
    })
    .catch((err) => {
      logger.error(
        'Error during Data Source initialization:\n',
        err.message,
        err.stack
      );
    });

  const commonRepository = myDataSource.getRepository(Common);
  const newCommon = new Common();
  newCommon.name = 'new user';
  await commonRepository.save(newCommon);
  await myDataSource.destroy();
};

Build

Then the build process generated the build/server.js file with the next content for require_common_entity var:

...
    Object.defineProperty(exports2, "__esModule", { value: true }); exports2.Common = void 0;
    require("reflect-metadata");
    var typeorm_1 = require("typeorm");
    var Common2 = class Common {
    };
    __decorate([
      (0, typeorm_1.PrimaryGeneratedColumn)("uuid")
    ], Common2.prototype, "id", void 0);
    __decorate([
      (0, typeorm_1.Column)({ type: "varchar", nullable: false })
    ], Common2.prototype, "name", void 0);
    Common2 = __decorate([
      (0, typeorm_1.Entity)()
    ], Common2);
    exports2.Common = Common2;

Problem

And finally, as we see the build has been processed correctly, the entity class is created and wrapped with decorator, but after the DB initialization, but the actual problem is that the created Common entity does not have metadata, which the typeorm code need for something:
EntityMetadataNotFoundError: No metadata for "Common" was found. at DataSource.getMetadata (/node_modules/typeorm/data-source/DataSource.js:304:19) although the exports2.Common class is present and log shows it.

Question

Thus, after searching the details of the error in the official docs and issues and on other platforms, i didn't find the actual cause and solution for my personal situation. Everything that any source of information suggests is already settled.

So what else do i need to make it work correctly and make entities to have needed metadata ?


Solution

  • Find out that inside the init function firstly i need to await the datasource initialization even though there's a thenable contract after:

    export const init = async () => {
      await myDataSource // <--
        .initialize()
      ...
    

    And only then inside the body of .initialization() function it tries (try/catch) to await this.buildMetadatas() which then compares the metadatas Map with provided entity to it :)