Getting error when I save() an entity - Error: Cyclic dependency: "e" - AWS serverless with typeORM

Viewed 190

Im doing a project with AWS serverless Node js with Typescript and TypeORM everithing looks like is working perfectly. Locally I can get normal or complex queries. But I got stuck when I started to crete the add/edit part in my application.

I'm trying to save my complete entity and it saves all the relations too. then I applied the cascade: true parameter to the entity properties and it works perfectly.

The problem begins when I deployed successful the app to the aws. All the queries works but especially at moment to save the entity tripLeg im getting this Error: Cyclic dependency: "e"

It only is happening when is deployed version, locally works perfectly. Do you know how to solve it?

Entity (Pay atention The last 3 properties )

import {
  Column,
  Entity,
  Index,
  JoinColumn,
  ManyToOne,
  OneToMany,
  OneToOne,
  PrimaryGeneratedColumn
} from 'typeorm';
import { Fbo } from './Fbo';
import { Airport } from './Airport';
import { Trip } from './Trip';
import { Pilot } from './Pilot';
import { TripLegClient } from './TripLegClient';
import { TripLegPassenger } from './TripLegPassenger';
import { TripLegStats } from './TripLegStats';

@Index('idx_unique_trip_legNo', ['legNo', 'tripId'], { unique: true })
@Index('IDX_d1cb2c6e35ce929f806af521c8', ['id'], { unique: true })
@Index('REL_d1cb2c6e35ce929f806af521c8', ['id'], { unique: true })
@Index('id', ['id'], {})
@Index('fk_fromId_airport_idx', ['fromId'], {})
@Index('fk_toId_airport_idx', ['toId'], {})
@Index('fk_fromFboId_fbo_idx', ['fromFboId'], {})
@Index('fk_toFboId_fbo_idx', ['toFboId'], {})
@Index('tripId_idx', ['tripId'], {})
@Index('fk_pic_idx', ['picId'], {})
@Index('fk_sic_idx', ['sicId'], {})
@Entity('trip_leg', { schema: 'schedule' })
export class TripLeg {
  @PrimaryGeneratedColumn({ type: 'int', name: 'id' })
  id: number;

  @Column('int', { name: 'leg_no' })
  legNo: number;

  @Column('varchar', { name: 'trip_id', length: 10 })
  tripId: string;

  @Column('varchar', { name: 'from_id', length: 7 })
  fromId: string;

  @Column('varchar', { name: 'to_id', length: 7 })
  toId: string;

  @Column('int', { name: 'from_fbo_id' })
  fromFboId: number;

  @Column('int', { name: 'to_fbo_id' })
  toFboId: number;

  @Column('int', { name: 'pic_id' })
  picId: number;

  @Column('int', { name: 'sic_id', nullable: true })
  sicId: number | null;

  @Column('datetime', { name: 'date_time' })
  dateTime: Date;

  @ManyToOne(() => Fbo, (fbo) => fbo.tripLegs, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION'
  })
  @JoinColumn([{ name: 'to_fbo_id', referencedColumnName: 'id' }])
  toFbo: Fbo;

  @ManyToOne(() => Airport, (airport) => airport.tripLegs, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION'
  })
  @JoinColumn([{ name: 'to_id', referencedColumnName: 'id' }])
  to: Airport;

  @ManyToOne(() => Trip, (trip) => trip.tripLegs, {
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE'
  })
  @JoinColumn([{ name: 'trip_id', referencedColumnName: 'id' }])
  trip: Trip;

  @ManyToOne(() => Pilot, (pilot) => pilot.tripLegs, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION'
  })
  @JoinColumn([{ name: 'sic_id', referencedColumnName: 'id' }])
  sic: Pilot;

  @ManyToOne(() => Pilot, (pilot) => pilot.tripLegs2, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION'
  })
  @JoinColumn([{ name: 'pic_id', referencedColumnName: 'id' }])
  pic: Pilot;

  @ManyToOne(() => Airport, (airport) => airport.tripLegs2, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION'
  })
  @JoinColumn([{ name: 'from_id', referencedColumnName: 'id' }])
  from: Airport;

  @ManyToOne(() => Fbo, (fbo) => fbo.tripLegs2, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION'
  })
  @JoinColumn([{ name: 'from_fbo_id', referencedColumnName: 'id' }])
  fromFbo: Fbo;

  @OneToMany(() => TripLegClient, (tripLegClient) => tripLegClient.tripLeg, {
    cascade: true
  })
  tripLegClients: TripLegClient[];

  @OneToMany(
    () => TripLegPassenger,
    (tripLegPassenger) => tripLegPassenger.tripLeg,
    { cascade: true }
  )
  tripLegPassengers: TripLegPassenger[];

  @OneToOne(() => TripLegStats, (tripLegStats) => tripLegStats.tripLeg, {
    cascade: true
  })
  tripLegStats: TripLegStats;
}

TypeORM Save() call

await conn.getRepository(TripLeg).save(tripLeg);

tsconfig.json

    {
        "compilerOptions": {
            "allowJs"                         : true,
            "allowSyntheticDefaultImports"    : true,
            "allowUnreachableCode"            : true,
            "alwaysStrict"                    : true,
            "baseUrl"                         : "./src/",
            "declaration"                     : true,
            "emitDecoratorMetadata"           : true,
            "esModuleInterop"                 : true,
            "experimentalDecorators"          : true,
            "forceConsistentCasingInFileNames": true,
            "importHelpers"                   : true,
            "inlineSources"                   : true,
            "isolatedModules"                 : true,
            "module"                          : "CommonJS",
            "moduleResolution"                : "node",
            "noFallthroughCasesInSwitch"      : true,
        

    "noImplicitAny"                   : true,
        "noImplicitReturns"               : true,
        "noImplicitThis"                  : true,
        "noUnusedLocals"                  : true,
        "noUnusedParameters"              : true,
        "outDir"                          : "build",
        "preserveConstEnums"              : true,
        "removeComments"                  : true,
        "resolveJsonModule"               : true,
        "skipLibCheck"                    : true,
        "sourceMap"                       : true,
        "strict"                          : true,
        "strictNullChecks"                : true,
        "target"                          : "ESNext",
        "types"                           : ["reflect-metadata", "node"],
        "lib"                             : ["ES5", "ES6", "ES7", "ESNext"],
        "strictPropertyInitialization"    : false,
        "paths"                           : {
            "@constants/*"   : ["constants/*"],
            "@constants"     : ["constants"],
            "@controllers/*" : ["controllers/*"],
            "@controllers"   : ["controllers"],
            "@db/*"          : ["db/*"],
            "@db"            : ["db"],
            "@handlers/*"    : ["handlers/*"],
            "@handlers"      : ["handlers"],
            "@helpers/*"     : ["helpers/*"],
            "@helpers"       : ["helpers"],
            "@interfaces/*"  : ["interfaces/*"],
            "@interfaces"    : ["interfaces"],
            "@middlewares/*" : ["middlewares/*"],
            "@middlewares"   : ["middlewares"],
            "@providers/*"   : ["providers/*"],
            "@providers"     : ["providers"],
            "@services/*"    : ["services/*"],
            "@services"      : ["services"],
            "@utils/*"       : ["utils/*"],
            "@utils"         : ["utils"],
            "@entities/*"    : ["entities/*"],
            "@entities"      : ["entities"],
            "@repositories/*":["repositories/*"],
            "@repositories"  :["repositories"]
        }
    },  
    "exclude": [        
        ".build/**/*",
        "build/**/*",
        "dist/**/*",
        "node_modules/**/*",
        ".serverless/**/*",
        ".webpack/**/*",
        "_warmup/**/*",
        ".vscode/**/*",
        "../webpack.config.js"
    ],
    "include"  : ["**/*.ts"],
    "typeRoots": ["node_modules/@types"]
 }
1 Answers

I had the same error. The problem was that the minimizer was assigning the same name to 2 different entity classes.

When you save an entity, TypeORM runs an algorithm to sort the graph of the relationships of your entities. If you have a relationship between 2 entities to which the minimizer assigned the same name, for example 'e', you will have an invalid edge ['e', 'e'] and the algorithm will throw the error Cyclic dependency: "e".

To solve this problem, I added a configuration in webpack.config.js to keep the names of the entity classes:

const TerserPlugin = require('terser-webpack-plugin');
optimization: {
    minimize: true,
    minimizer: [new TerserPlugin({ terserOptions: { keep_classnames: <regex> }})],
  }

where regex is a regular expression that matches with all my entity classes. You can also keep the name of all the classes of your code using true.

Related