In NestJS 10 with TypeORM, how do I update a field in my entity's @OneToOne member?

133 Views Asked by At

I'm using NestJS 10 with TypeORM 0.3.17. I have defined this entity wiht a @OneToOne relationship

@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

    ...
  @Column()
  password: string;

  @OneToOne(() => Contact, {
    cascade: true,
    onDelete: 'CASCADE',
    nullable: true,
  })
  @JoinColumn({ name: 'contact_id', referencedColumnName: 'id' })
  contact?: Contact;

}

I have defined a corresponding UpdateUserDto and CreateUserDto ...

export class UpdateUserDto extends PartialType(CreateUserDto) {
}

export class CreateUserDto {
  email: string;
  password: string;
  refreshToken: string;
  contact: CreateContactDto;
}

How do I create a service method to update a single field from my Contact object? I tried this

export class UsersService {
  constructor(
    private readonly userRepository: UserRepository, // import as usual
  ) {}
    ...
  async update(
    id: string,
    updateUserDto: UpdateUserDto,
  ): Promise<UpdateResult> {
    return this.userRepository.update(id, updateUserDto);
  }

but when I call this method with the parameters

this.usersService.update(userId, {
  contact: {
    firstName: 'Dave',
    lastName: 'Test',
    mailingAddress: {
      addressLine1: '321 oklahoma',
      city: 'Springfield',
      zipCode: '90210',
      region: { id: '123' }
    }
  }
});

It fails with the runtime error

[ExceptionsHandler] Property "contact.firstName" was not found in "User". Make sure your query is correct.

Edit: Adding contact entity ...

import { Address } from 'src/addresses/entities/address.entity';
import { User } from 'src/users/entities/user.entity';
import {
  Column,
  Entity,
  JoinColumn,
  ManyToOne,
  OneToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';

@Entity()
export class Contact {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

...

  @OneToOne((type) => Address, {
    cascade: true,
    onDelete: 'CASCADE',
  })
  @JoinColumn({ name: 'mailing_address_id', referencedColumnName: 'id' })
  mailingAddress: Address;

}
3

There are 3 best solutions below

1
Majva On

First of all, find the user by ID:

const user = this.userRepository.findOneBy({ id });
if (!user) throw new NotFoundException();

Then create Contact Object;

    const contact = new Contact();
    contact.firstName = "Dave";
    ...

And finally, you can save the relation :

    user.contact = contact;
    userRepository.save(user);
6
Hai Alison On

in my case, I will do this:

const user = await User.findOneOrFail({
  where: { id || IsNull() }, //in case of id null and not getting the first row in db
  relations: ['contact'],
});
Object.assign(user.contact, {
  firstName: 'Dave',
  lastName: 'test',
  //...your rest
}); //if the user doesn't have a contact, it will create a new one
await user.save();

if you want to update multiple relations nested user Entity

Object.assign(user, {
        contact: {
          ...user.contact,
          firstName: firstName,
          lastname: lastName,
          //...your rest
        },
        //...your object
      }); 

remember to add relations and your relation must have cascade

3
OMartinez-NeT On

The issue here is that you declared @JoinColumn in both sides in Contact and User. Read one-to-one-relations:

@JoinColumn which is required and must be set only on one side of the relation

From your model It seems @JoinColumn has to be in the Contact entity instead of user. Also you should check if owner_id is the correct FK that connects to User. Remember with this definition:

  @OneToOne(() => User, { nullable: true })
  @JoinColumn({ name: 'owner_id', referencedColumnName: 'id' })
  owner?: User;

You're saying "Get the user related to this contact where my FK is owner_id (meaning owner_id has to exist in contact) and the column in User is id.