next.jsforeign-keysmany-to-manyentitytypeorm

Three/four-level entity relationship: Gallery/Exhibit/Painting/ExhibitPaintingDetail


ERD for tables involved here is at https://4l3c78.axshare.com/#g=1&p=data

This is a nextjs/typeorm project. How are the relationships in the entities Exhibit, Painting, ExhibitPaintingDetail? The Postgres tables include foreign keys from Painting and Exhibit to ExhibitPaintingDetail.

Exhibit entity:


  @ManyToOne(() => Gallery, (gallery) => gallery.exhibits)
  gallery: Gallery;

  @ManyToMany(() => Painting, (painting) => painting.exhibits)
  @JoinTable({ name: "exhibit_paintings" }) // Specify the name of the junction table
  paintings?: Painting[];

  @OneToMany(
    () => ExhibitPaintingDetail,
    (exhibitPaintingDetail) => exhibitPaintingDetail.exhibitId
  )
  exhibitPaintingDetails: ExhibitPaintingDetail[];

The Painting entity:


  @ManyToMany(() => Exhibit, (exhibit) => exhibit.paintings)
  @JoinTable({ name: "exhibit_paintings" }) // Specify the name of the junction table
  exhibits: Exhibit[];

  @ManyToOne(() => Person, (person) => person.paintings)
  @JoinColumn({ name: "artistId" })
  person: Promise<Person>;
  exhibitRelations: any;

  @OneToMany(
    () => ExhibitPaintingDetail,
    (exhibitPaintingDetail) => exhibitPaintingDetail.paintingId
  )
  exhibitPaintingDetails: ExhibitPaintingDetail[];

The ExhibitPaintingDetail table:


  @PrimaryColumn()
  exhibitId: number;
  @PrimaryColumn()
  paintingId: number;

  @ManyToOne((type) => Exhibit, (exhibit) => exhibit.exhibitPaintingDetails)
  @JoinColumn({ name: "exhibitId" })
  exhibit: Exhibit;

  @ManyToOne((type) => Painting, (painting) => painting.exhibitPaintingDetails)
  @JoinColumn({ name: "paintingId" })
  painting: Painting;

  @OneToOne(() => ExhibitPaintings, (exhibitPaintings) => exhibitPaintings.id)
  @JoinColumn({ name: "exhibitpaintingId" })
  exhibitPaintings: ExhibitPaintings;

nextjs is reporting this error from the ExhibitPaintingDetail entity wherever:

Error: Cannot access 'Exhibit' before initialization

on the


Solution

  • These are the entities that now work 100% of the time in localhost dev and 90% of the time on vercel production:

    Exhibit:

        import {
          Entity,
          PrimaryGeneratedColumn,
          Column,
          ManyToOne,
          //ManyToMany,
          //OneToOne,
          //JoinTable,
          OneToMany,
          JoinColumn,
          //Relation,
        } from "typeorm";
        //import * as typeorm from "typeorm";
        import { Gallery } from "./Gallery";
        //import { Painting } from "./Painting";
        import { ExhibitPaintingDetail } from "./ExhibitPaintingDetail";
        
        @Entity("exhibit")
        export class Exhibit {
          @PrimaryGeneratedColumn()
          id?: number;
        
          @Column()
          name: string;
        
          @Column({ nullable: true })
          description: string;
        
          @Column({ default: true })
          isEnabled?: boolean;
        
          @Column({ nullable: true })
          startDate: Date;
        
          @Column({ nullable: true })
          endDate: Date;
        
          @Column({ nullable: true })
          dropOffDate: Date;
        
          @Column({ nullable: true })
          pickUpDate: Date;
        
          @Column({ nullable: true })
          galleryId: number;
        
          @Column({ nullable: true })
          note: string;
        
          @Column({ nullable: true })
          decisionDate?: Date;
        
          @Column({ nullable: true })
          website: string;
        
          @ManyToOne(() => Gallery, (gallery) => gallery.exhibits, {})
          @JoinColumn({ name: "galleryId" })
          gallery: Promise<Gallery>;
        
          @OneToMany(
            () => ExhibitPaintingDetail,
            (exhibitPaintingDetail) => exhibitPaintingDetail.exhibit
          )
          exhibitPaintingDetails: ExhibitPaintingDetail[];
        }
    

    Painting:

        import {
          Entity,
          PrimaryGeneratedColumn,
          Column,
          ManyToMany,
          JoinTable,
          OneToMany,
        } from "typeorm";
        import { Exhibit } from "./Exhibit";
        import { Person } from "./Person";
        import { ExhibitPaintingDetail } from "./ExhibitPaintingDetail";
        import { Tag } from "./Tag";
        import { dateTimeRangeList } from "aws-sdk/clients/health";
        
        @Entity("painting")
        export class Painting {
          @PrimaryGeneratedColumn()
          id: number;
        
          @Column()
          title: string;
        
          @Column()
          picture: string;
        
          @Column()
          description: string;
        
          @Column("int")
          artistId: number;
        
          @Column("int")
          month: number;
        
          @Column()
          year: number;
        
          @Column()
          media: string;
        
          @Column()
          price: number;
        
          @Column()
          width: number;
        
          @Column()
          height: number;
        
          @Column()
          sold: number;
        
          @Column({ nullable: true })
          depth: number;
        
          @Column({ nullable: true })
          note: string;
        
          @Column({ nullable: true })
          api_id: number;
        
          @Column({ nullable: true })
          permalink: string;
        
          @Column("timestamp without time zone", { nullable: true })
          timestamp: Date;
        
          @OneToMany(
            () => ExhibitPaintingDetail,
            (exhibitPaintingDetail) => exhibitPaintingDetail.painting
          )
          exhibitPaintingDetails: ExhibitPaintingDetail[];
        
          @ManyToMany(() => Tag, (tag) => tag.paintings)
          @JoinTable({
            name: "tagpaintings", // name of the join table
            joinColumn: {
              name: "paintingid", // name of the column in the join table that references the Painting entity
              referencedColumnName: "id", // name of the id column in the Painting entity
            },
            inverseJoinColumn: {
              name: "tagid", // name of the column in the join table that references the Tag entity
              referencedColumnName: "id", // name of the id column in the Tag entity
            },
          })
          tags: Tag[];
        }
    

    ExhibitPaintingDetail:

        import {
          Entity,
          PrimaryGeneratedColumn,
          Column,
          JoinColumn,
          ManyToOne,
        } from "typeorm";
        import { Exhibit } from "./Exhibit";
        import { Painting } from "./Painting";
        
        @Entity("exhibitpaintingdetail")
        export class ExhibitPaintingDetail {
          @PrimaryGeneratedColumn()
          id?: number;
        
          @Column({ nullable: true })
          exhibitpaintingid: number;
        
          @Column({ nullable: true })
          submissiondate: Date;
        
          @Column({ nullable: true })
          exhibitpaintingprice: number;
        
          @Column({ nullable: true })
          status: string;
        
          @Column({ nullable: true })
          outcome: string;
        
          @Column({ nullable: true })
          note: string;
        
          // @Column()
          // exhibitid: number;
        
          // @Column()
          // paintingid: number;
        
          @ManyToOne(() => Exhibit, (exhibit) => exhibit.exhibitPaintingDetails)
          @JoinColumn({ name: "exhibitid" })
          exhibit: any;
        
          @ManyToOne(() => Painting, (painting) => painting.exhibitPaintingDetails)
          @JoinColumn({ name: "paintingid" })
          painting: any;
        }
    
    

    Finally, I learned that using string literals instead of direct references to related entities in the ManyToOne relations was a key change:

          @ManyToOne(() => "Exhibit", (exhibit) => exhibit.exhibitPaintingDetails)
          @JoinColumn({ name: "exhibitid" })
          exhibit: any;
        
          @ManyToOne(() => "Painting", (painting) => painting.exhibitPaintingDetails)
          @JoinColumn({ name: "paintingid" })
          painting: any;
        }