Why does the following typescript typeorm setup not work in production, but does work in development?
Versions in use:
-NodeJS I’m using is v16.20.2
-Typescript – 5.2.2
-Operating system is Windows
Database I’m using is SQL Server (mssql). The database is the same for both environments.
The package.json in the root of the project has the following scripts block:
"scripts": { "dev": "next dev", "build": "cd server_components/entities && tsc && cd ../../ && next build", "start": "next start -p 80", }
The tsconfig.json in the ./server_components/entities has the following code;
{ "compilerOptions": { "target": "es6", "module": "ESNext", "moduleResolution": "node", "esModuleInterop": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "outDir": "lib" } }
I always run npm run build first. Then;
If I run npm run dev, the code works perfectly. If I run npm start, the code results in the error;
TypeORMError: Entity metadata for l#owningFault was not found. Check if you specified a correct entity object and if it’s connected in the connection options.
NOTE: Both commands (npm run dev and npm start) are being run on my local machine so it’s not as if there’s a different OS or Node version being used.
This is my Base.ts file;
@Entity({ name: 'STBaseFieldEntity' })
export default class STBaseFieldEntity extends BaseEntity {
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
Created: string
@Column({ type: 'nvarchar', length: 50 })
CreatedBy: string
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
LastModified: string
@Column({ type: 'nvarchar', length: 50 })
LastModifiedBy: string
}
This is my Fault.Guide.Base.ts file;
import 'reflect-metadata';
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, TableInheritance, EntityManager, EntitySchema } from 'typeorm';
import { STFaultGuideCheck, FaultGuideCheckDTO } from './Fault.Guide.Check';
import { STFaultGuideCriteria, FaultGuideCriteriaDTO } from './Fault.Guide.Criteria';
import { isLike } from './App.ORM.Base';
import STBaseFieldEntity from './Base';
@Entity({ name: 'STFaultGuide' })
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export class STFaultGuideBase extends STBaseFieldEntity {
private readonly entityManager: EntityManager;
@PrimaryGeneratedColumn()
FaultID: number
@Column({ type: 'int' })
ToolID: number
@Column({ type: 'nvarchar', length: 150 })
Fault: string
@Column({ type: 'nvarchar', length: 150 })
FaultDescription: string
@Column()
isActive: boolean
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
Created: string
@Column({ type: 'nvarchar', length: 50 })
CreatedBy: string
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
LastModified: string
@Column({ type: 'nvarchar', length: 50 })
LastModifiedBy: string
@Column({ type: 'nvarchar', length: 150, nullable: true })
Tooltip: string
@Column({ type: 'nvarchar', length: 30 })
descriminator: string
@OneToMany(() => STFaultGuideCheck, check => check.owningFault, { lazy: true })
Checks: Promise<STFaultGuideCheck[]> | any[]
@OneToMany(() => STFaultGuideCriteria, criteria => criteria.owningFault, { lazy: true })
Criteria: Promise<STFaultGuideCriteria[]> | any[]
static async search({ Keyword, descriminator }){
const value = isLike(Keyword);
const results = await this.find({
where: [
{ Fault: value, descriminator },
{ FaultDescription: value, descriminator }
]
});
// Fetch related data for each result
const promises = results.map(async result => {
let newResult = { ...result }
newResult.Checks = await result.Checks;
newResult.Criteria = await result.Criteria;
return newResult;
});
const populated = await Promise.all(promises);
return {
Data: populated
}
}
}
export class STACUC8FaultGuide extends STFaultGuideBase {
constructor(){
super()
this.descriminator = 'ACUC8FaultGuide'
}
}
export class STWCUC8FaultGuide extends STFaultGuideBase {
constructor(){
super()
this.descriminator = 'ACUC8FaultGuide'
}
}
This is my Fault.Guide.Check.ts file;
import 'reflect-metadata';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, BaseEntity, TableInheritance, JoinColumn } from 'typeorm';
import { IsIn } from 'class-validator';
import type { STFaultGuideBase } from './Fault.Guide.Base';
@Entity({ name: 'STFaultGuideCheck' })
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export abstract class STFaultGuideCheck extends BaseEntity {
@PrimaryGeneratedColumn()
FaultCheckID: number
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
Created: string
@Column({ type: 'nvarchar', length: 50 })
CreatedBy: string
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
LastModified: string
@Column({ type: 'nvarchar', length: 50 })
LastModifiedBy: string
@Column({ type: 'nvarchar', length: 200 })
Text: string
@Column({ type: 'nvarchar', length: 150, nullable: true })
Tooltip: string
@Column({ type: 'nvarchar', nullable: false })
@IsIn([ 'air', 'water' ])
GuideType: string
@Column({ type: 'nvarchar', length: 150 })
CheckType: string
@Column({ type: 'nvarchar', length: 30, select: false })
descriminator: string
@ManyToOne('STFaultGuideBase', (faultGuide: STFaultGuideBase) => faultGuide.Checks, { lazy: true })
@JoinColumn({ name: 'owningFaultFaultID', referencedColumnName: 'FaultID' })
owningFault: Promise<STFaultGuideBase>
}
@Entity()
export class STWCUC8FaultGuideCheck extends STFaultGuideCheck {
constructor(){
super()
this.descriminator = 'WCUC8FaultGuideCheck'
}
}
@Entity()
export class STACUC8FaultGuideCheck extends STFaultGuideCheck {
constructor(){
super()
this.descriminator = 'ACUC8FaultGuideCheck'
}
}
And this is my Fault.Guide.Criteria file;
import 'reflect-metadata';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, BaseEntity, TableInheritance, JoinColumn } from 'typeorm';
import type { STFaultGuideBase } from './Fault.Guide.Base';
@Entity({ name: 'STFaultGuideCriteria' })
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export abstract class STFaultGuideCriteria extends BaseEntity {
@PrimaryGeneratedColumn()
FaultCriteriaID: number
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
Created: string
@Column({ type: 'nvarchar', length: 50 })
CreatedBy: string
@Column({ type: 'datetime', default: 'GETUTCDATE()' })
LastModified: string
@Column({ type: 'nvarchar', length: 50 })
LastModifiedBy: string
@Column({ type: 'nvarchar', length: 10 })
CriteriaType: string
@Column({ type: 'nvarchar', length: 30, nullable: true })
Name: string
@Column({ type: 'nvarchar', length: 2000 })
Description: string
@Column({ type: 'nvarchar', length: 150, nullable: true })
Tooltip: string
@Column({ type: 'nvarchar', length: 30, select: false })
descriminator: string
@ManyToOne('STFaultGuideBase', (fault: STFaultGuideBase) => fault.Criteria, { lazy: true })
@JoinColumn({ name: 'owningFaultFaultID', referencedColumnName: 'FaultID' })
owningFault: Promise<STFaultGuideBase>;
}
@Entity()
export class STACUC8FaultGuideCriteria extends STFaultGuideCriteria {
constructor(){
super()
this.descriminator = 'ACUC8FaultGuideCriteria'
}
}
@Entity()
export class STWCUC8FaultGuideCriteria extends STFaultGuideCriteria {
constructor(){
super()
this.descriminator = 'WCUC8FaultGuideCriteria'
}
}
Lastly, this is my ORM.DataSource.ts file;
import { DataSource } from 'typeorm';
import STTool from './Tool.Base';
import { STModel } from './Model';
import { STFaultGuideBase } from './Fault.Guide.Base';
import { STFaultGuideCriteria } from './Fault.Guide.Criteria';
import { STFaultGuideCheck } from './Fault.Guide.Check';
const ApplicationDataSource = new DataSource({
type: 'mssql',
host: 'REDACTED',
username: 'REDACTED',
password: 'REDACTED',
database: 'REDACTED',
schema: 'REDACTED',
synchronize: true,
logging: true,
entities: [
STTool,
STModel,
STFaultGuideBase,
STFaultGuideCriteria,
STFaultGuideCheck
],
options: {
appName: 'REDACTED',
trustServerCertificate: true
}
});
export default ApplicationDataSource
I’ve double-checked all of the entities and ensured they have the Entity() decorator, which they do.
I’ve tried taking the Entity() decorator off the; STACUC8FaultGuideCriteria, STWCUC8FaultGuideCriteria, STWCUC8FaultGuideCheck and STACUC8FaultGuideCheck as these are not actually created as tables in the database, they’re used as differentiators.
I’ve tried adding STACUC8FaultGuideCriteria, STWCUC8FaultGuideCriteria, STWCUC8FaultGuideCheck and STACUC8FaultGuideCheck to the entities array in the DataSource.
I’ve ensured all the entities are specified in the DataSource.
I’ve been sure to synchronize the changes to the database.
I’ve tried to resolve the circular dependencies using lazy loading.
I’ve tried both adding and removing the JoinTable() decorator.
Again, even with all the changes above, they all worked in development but regardless of what I do, I still receive the error TypeORMError: Entity metadata for l#owningFault was not found. Check if you specified a correct entity object and if it’s connected in the connection options. when building for production.
user24810540 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.