Search code examples
node.jstypescriptmany-to-manytypeormnode.js-typeorm

Node.JS Typescript TypeORM ManyToMany relations add a relation and update the entity


I have the following typescript TypeORM model:

@Entity()
export class Teacher extends EntityBase {
    @Column({ length: 256 })
    public firstName: string

    @Column({ length: 256 })
    public lastName: string

    @Column({ unique: true, length: 256 })
    public email: string

    @ManyToMany((type) => Student, (student) => student.teachers, { nullable: true })
    @JoinTable()
    public students!: Student[]

    constructor(first: string, last: string, email: string) {
        super();
        this.firstName = first;
        this.lastName = last;
        this.email = email;
        //this.students = []; TypeORM initialization failed! InitializedRelationError: Array initializations are not allowed in entity relations
    }
}

The following query:

    public async AddStudent (teacher: Teacher, student: Student): Promise<Teacher | null> {
        if (teacher && student) {
            try {
                teacher.students.push(student);
                return await this.Update(teacher);
            } catch (e) { console.log(e); }
        }
        return null;
    }

This is what the Update does:

    public async Update (entity: T): Promise<T | null> {
        return await this._repository.save(entity);
    }

It failed with exception:

QueryFailedError: duplicate key value violates unique constraint "PK_ec87c1e05d0d8cefa233575cfe1"
    at PostgresQueryRunner.query (/usr/src/Node.JSRestAPI/node_modules/typeorm/driver/postgres/PostgresQueryRunner.js:219:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async InsertQueryBuilder.execute (/usr/src/Node.JSRestAPI/node_modules/typeorm/query-builder/InsertQueryBuilder.js:106:33)
    at async SubjectExecutor.executeInsertOperations (/usr/src/Node.JSRestAPI/node_modules/typeorm/persistence/SubjectExecutor.js:260:42)
    at async SubjectExecutor.execute (/usr/src/Node.JSRestAPI/node_modules/typeorm/persistence/SubjectExecutor.js:92:9)
    at async EntityPersistExecutor.execute (/usr/src/Node.JSRestAPI/node_modules/typeorm/persistence/EntityPersistExecutor.js:140:21)
    at async StudentRepository.Update (file:///usr/src/Node.JSRestAPI/src/infrastructure/build/index.js:107:16)
    at async StudentRepository.AddTeacher (file:///usr/src/Node.JSRestAPI/src/infrastructure/build/index.js:147:24)
    at async Promise.allSettled (index 1)
    at async AddStudentsToTeacherUseCase.Handle (file:///usr/src/Node.JSRestAPI/src/webapi.core/build/index.js:424:50) {
  query: 'INSERT INTO "student_teachers_teacher"("studentId", "teacherId") VALUES ($1, $2)',
  parameters: [ 1, 1 ],
  driverError: error: duplicate key value violates unique constraint "PK_ec87c1e05d0d8cefa233575cfe1"
      at /usr/src/Node.JSRestAPI/node_modules/pg/lib/client.js:526:17
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async PostgresQueryRunner.query (/usr/src/Node.JSRestAPI/node_modules/typeorm/driver/postgres/PostgresQueryRunner.js:184:25)
      at async InsertQueryBuilder.execute (/usr/src/Node.JSRestAPI/node_modules/typeorm/query-builder/InsertQueryBuilder.js:106:33)
      at async SubjectExecutor.executeInsertOperations (/usr/src/Node.JSRestAPI/node_modules/typeorm/persistence/SubjectExecutor.js:260:42)
      at async SubjectExecutor.execute (/usr/src/Node.JSRestAPI/node_modules/typeorm/persistence/SubjectExecutor.js:92:9)
      at async EntityPersistExecutor.execute (/usr/src/Node.JSRestAPI/node_modules/typeorm/persistence/EntityPersistExecutor.js:140:21)
      at async StudentRepository.Update (file:///usr/src/Node.JSRestAPI/src/infrastructure/build/index.js:107:16)
      at async StudentRepository.AddTeacher (file:///usr/src/Node.JSRestAPI/src/infrastructure/build/index.js:147:24)
      at async Promise.allSettled (index 1) {
    length: 264,
    severity: 'ERROR',
    code: '23505',
    detail: 'Key ("studentId", "teacherId")=(1, 1) already exists.',
    hint: undefined,
    position: undefined,
    internalPosition: undefined,
    internalQuery: undefined,
    where: undefined,
    schema: 'public',
    table: 'student_teachers_teacher',
    column: undefined,
    dataType: undefined,
    constraint: 'PK_ec87c1e05d0d8cefa233575cfe1',
    file: 'nbtinsert.c',
    line: '663',
    routine: '_bt_check_unique'
  },
  length: 264,
  severity: 'ERROR',
  code: '23505',
  detail: 'Key ("studentId", "teacherId")=(1, 1) already exists.',
  hint: undefined,
  position: undefined,
  internalPosition: undefined,
  internalQuery: undefined,
  where: undefined,
  schema: 'public',
  table: 'student_teachers_teacher',
  column: undefined,
  dataType: undefined,
  constraint: 'PK_ec87c1e05d0d8cefa233575cfe1',
  file: 'nbtinsert.c',
  line: '663',
  routine: '_bt_check_unique'
}
TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Teacher'
    |     property 'students' -> object with constructor 'Array'
    |     index 0 -> object with constructor 'Student'
    |     property 'teachers' -> object with constructor 'Array'
    --- index 0 closes the circle
    at JSON.stringify (<anonymous>)
    at AddStudentsToTeacherUseCase.Handle (file:///usr/src/Node.JSRestAPI/src/webapi.core/build/index.js:425:76)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async AddStudentsToTeacherController.AddStudentsToTeacher (file:///usr/src/Node.JSRestAPI/build/src/webapi/Controllers/AddStudentsToTeacherController.js:47:21)
::1 - - [06/Jul/2024:09:15:53 +0000] "POST /api/addstudents HTTP/1.1" 400 66 "-" "PostmanRuntime/7.29.4"

Solution

  • JoinTable must only be on ONE side of the relation.