How do I properly update entity relationships in Typeorm

Viewed 1159

I have a basic User table in which they can have proficiencies in various musical Instruments. I'm having trouble figuring out the correct way to make a basic updateUser function in which they can update their User information, as well as their instrument proficiencies using Typeorm and a MySQL database.

User Class

@Entity()
@ObjectType()
export class User extends BaseEntity {

  public constructor(init?:Partial<User>) {
      super();
      Object.assign(this, init);
  }

  @Index({ unique: true})
  @Column()
  email: string;

  @Index({ unique: true})
  @Column()
  username: string;

  @Column()
  @HideField()
  password: string;

  @Column({
      nullable: true
  })
  profilePicture?: string;

  @Index({ unique: true})
  @Column()
  phoneNumber: string;

  //Lazy loading
  @OneToMany(() => UserInstrument, p => p.user, {
      cascade: true
  })
  instruments?: Promise<UserInstrument[]>;
}

User Instrument Class

@Entity()
@ObjectType()
export class UserInstrument extends BaseEntity {

  public constructor(init?:Partial<UserInstrument | InstrumentProficiencyInput>) {
      super();
      Object.assign(this, init);
  }

  @Column({
      type: 'enum',
      enum: Instrument
  })
  instrument : Instrument

  @Column()
  proficiency : number;

  @JoinColumn()
  @HideField()
  userId : number;

  @ManyToOne(() => User, p => p.instruments)
  @HideField()
  user : User;
}

Now creating a new user with predefined instruments hasn't been an issue. I can simply insert a new User and it will autofill the Id and UserId fields for the appropriate tables.

  async create(request: RegisterUserInput) : Promise<boolean>{
    const user        = new User();
    user.username     = request.username;
    user.password     = request.password;
    user.phoneNumber  = request.phoneNumber;
    user.email        = request.email;
    user.instruments  = Promise.resolve(request.instruments?.map(p => new UserInstrument(p)));

    const result = await this.usersRepository.save(user);
}

The issue

Now whenever I do something similar to Update the User/Instrument tables I get a "ER_BAD_NULL_ERROR: Column 'userId' cannot be null" exception

  async updateUser(request: UpdateUserInput, id : number): Promise<boolean> {
    var user = new User(classToPlain(request));
    user.id = id;
    user.instruments = Promise.resolve(request.instruments?.map(p => new UserInstrument(p)));

    (await user.instruments)?.forEach(p => p.userId = id);

    await this.usersRepository.save(user);

    return true;
}

This code generates the following exception

code:'ER_BAD_NULL_ERROR'
errno:1048
index:0
message:'ER_BAD_NULL_ERROR: Column 'userId' cannot be null'
name:'QueryFailedError'
parameters:(2) [null, 8]
query:'UPDATE `user_instrument` SET `userId` = ? WHERE `id` = ?'
sql:'UPDATE `user_instrument` SET `userId` = NULL WHERE `id` = 8'
sqlMessage:'Column 'userId' cannot be null'
sqlState:'23000'
stack:'QueryFailedError: ER_BAD_NULL_ERROR: Column 'userId' cannot be null

Even though when I inspect my user object I can see the userId field is set correctly

__has_instruments__:true
__instruments__:(1) [UserInstrument]
    0:UserInstrument {created: '2020-11-29 02:46:10', instrument: 'Guitar', proficiency: 5, userId: 30}
length:1

So what am I doing wrong? Is there a more preferred way to update the users Instrument table, without accessing the Instrument repository directly? I'm not sure why the initial save on my create method works, but not on update.

0 Answers
Related