Search code examples
nestjsmetadatatypeormserverless

NestJS - No metadata for "<Entity>" was found


I am using a simple NestJS App integrated into an AWS-Lambda function.

A TypeORM DataSource-Object is used for Database-Access.

export const AppDataSource = new DataSource({
    type: 'mysql',
    host: process.env.DATABASE_URL,
    port: parseInt(process.env.DATABASE_PORT),
    username: 'root',
    password: 'root',
    database: 'db',
    entities: [__dirname + '/*/entities/*.entity.js'],
    migrations: [__dirname + '/migrations/*.js'],
    synchronize: false
});

I have only one Entity:

@Entity()
export class Code {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    code: string;
}

The compiled entity is located in dist/code/entities/code.entity.js. On runtime my NestJS-App is working inside "dist", so the entities-value on DataSource should be correct.

If I try to create a Code-Entity by calling POST on my CodeService the following is logged:

First there is a bunch of stuff from serverless

[Nest] 43723  - 27/07/2022, 20:07:04   ERROR [ExceptionsHandler] No metadata for "Code" was found.
(λ: main) RequestId: 39c034a8-4c72-403a-98f3-5dd2a829cc0d  Duration: 806.10 ms  Billed Duration: 807 ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [InstanceLoader] AppModule dependencies initialized +14ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [InstanceLoader] ConfigHostModule dependencies initialized +0ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [InstanceLoader] CodeModule dependencies initialized +0ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [InstanceLoader] ConfigModule dependencies initialized +1ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [RoutesResolver] CodeController {/code}: +3ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [RouterExplorer] Mapped {/code, POST} route +2ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [RouterExplorer] Mapped {/code, GET} route +1ms
[Nest] 43723  - 27/07/2022, 20:07:04     LOG [NestApplication] Nest application successfully started +1ms

And directly below, this error is shown:

EntityMetadataNotFoundError: No metadata for "Code" was found.
    at DataSource.getMetadata (<path to project>/node_modules/typeorm/data-source/DataSource.js:286:19)
    at Repository.get metadata [as metadata] (<path to project>/node_modules/typeorm/repository/Repository.js:23:40)
    at Repository.create (<path to project>/node_modules/typeorm/repository/Repository.js:52:41)
    at CodeService.create (<path to project>/dist/code/code.service.js:22:38)
    at CodeController.create (<path to project>/dist/code/code.controller.js:25:33)
    at <path to project>/node_modules/@nestjs/core/router/router-execution-context.js:38:29
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async <path to project>/node_modules/@nestjs/core/router/router-execution-context.js:46:28
    at async <path to project>/node_modules/@nestjs/core/router/router-proxy.js:9:17

All potential solutions I have come across involve either correcting the DataSource entities-value to target the correct directory, include .js-files (not .ts) or adding relevant TypeORM-Annotations.

When I check dist/code/entities/code.entity.js the Metadata is clearly added:

let Code = class Code {
};
__decorate([
    (0, typeorm_1.PrimaryGeneratedColumn)(),
    __metadata("design:type", Number)
], Code.prototype, "id", void 0);
__decorate([
    (0, typeorm_1.Column)(),
    __metadata("design:type", String)
], Code.prototype, "code", void 0);
Code = __decorate([
    (0, typeorm_1.Entity)()
], Code);
exports.Code = Code;

My assumption is that, as shown in the Log for the EntityMetadataNotFoundError, there may be some discrepancy between working from the dist directory and still using the node_modules directory. I would assume that on compile-time every imported module is added to the dist directoy?

Honestly though, I have no idea howto fix this MetaData-Error...I appreciate every idea to solve this issue.


Solution

  • Alright, after trial and error the issue lies with the initialization of the DataSource, because executing a request twice resolves without any MetaDataNotFoundError.

    When the CodeService is instantiated, the CodeRepository is retrieved from the DataSource:

    export class CodeService {
    
      private _repository: Repository<Code>;
    
      constructor() {
        this.repository = AppDataSource.getRepository(Code);
      }
    

    The problem is that with serverless, the application is bootstrapped when a request is executed. A normal app would bootstrap on startup which would usually leave enough time for the DataSource to initialize before the first request is executed.

    The lifecycle with serverless though is much more immediate:

    Client-Request arrives at Lambda -> Handler bootstraps App -> Request is delegated to handler -> handler calls Service -> Service tries to access MetaData but does not find any, because the DataSource is still initializing (from bootstrap() to here only a few milliseconds passed)

    So the quite obvious solution to this problem is added to main.ts:

    async function bootstrap(): Promise<Handler> {
      if (AppDataSource.isInitialized === false)
        await AppDataSource.initialize();