Search code examples
typescriptpostgresqlnestjstypeorm

TypeORM insert row with one to one relationship


I have three tables user, userCareerProfile, userPersonalProfile with @OneToOne relationship.

User Entity:

@Entity({ name: 'users' })
export class User extends AbstractEntity {
    @Column({ unique: true })
    email: string;

    @Column({ type: 'varchar', length: 50 })
    full_name: string;

    @OneToOne(() => UserPersonalProfile, (details) => details.user)
    personal_details: UserPersonalProfile;

    @OneToOne(() => UserCareerProfile, (career) => career.user)
    career_profile: UserCareerProfile;

}

Personal Profile Entity:

@Entity()
export class UserPersonalProfile extends AbstractEntity {

    @Column({ type: 'varchar', length: 20, nullable: true })
    date_of_birth: string;

    @Column({ type: 'varchar', length: 200, nullable: true })
    address: string;

    .....

    @OneToOne(() => User, (user) => user.personal_details, {
        onDelete: 'CASCADE',
    })
    @JoinColumn({ name: 'user_id' })
    user: User;

}

Career Profile Entity:

@Entity()
export class UserCareerProfile extends AbstractEntity {

    @Column({ type: 'varchar', length: 100, nullable: true })
    job_role: string;

    @Column({ type: 'varchar', length: 100, nullable: true })
    work_location: string;

   .....

    @OneToOne(() => User, (user) => user.career_profile, {
        onDelete: 'CASCADE',
    })
    @JoinColumn({ name: 'user_id' })
    user: User;

}

Problem: When I am creating a new user, a new row is been adding to the user table but there is no record inserted in the other two tables. How can I be able to insert rows in the two tables based on the newly created user?

User Service:

public async createUser(userAttrs: Partial<User>): Promise<User> {
     const user = await this._usersRepository.save(
         this._usersRepository.create({
            ...userAttrs,
         }),
     );

     return user;
}

Solution

  • On your User entity specify the cascade option on the @OneToOne() decorator. You can set it to true, insert, update, remove, soft-remove or recover. Choose the required restriction for your use case. Setting it to true or insert should insert the related object in the database.

    Also apply the @JoinColumn() decorator to this side of the relation. That means that this table will contain a "relation id"and a foreign key to the other table.

    @Entity()
    export class User {
        @PrimaryGeneratedColumn()
        id: number;
    
        @Column({ unique: true })
        email: string;
    
        @Column({ type: 'varchar', length: 50 })
        name: string;
    
        @OneToOne(() => Profile, (profile) => profile.user, { cascade: true })
        @JoinColumn()
        profile: Profile;
    }
    

    Here's an example Profile entity.

    @Entity()
    export class Profile {
        @PrimaryGeneratedColumn()
        id: number;
    
        @Column({ type: 'varchar', length: 20, nullable: true })
        date_of_birth: string;
    
        @OneToOne(() => User, (user) => user.profile)
        user: User;
    }
    

    Then use a repository to save a new User instance.

    For example:

    const profile: Profile = new Profile();
    profile.date_of_birth = '01/01/1984';
    
    const user: User = new User();
    user.email = '[email protected]';
    user.name = 'John';
    user.profile = profile;
    
    await this.userRepository.save(user);
    

    This will persist the user and the profile. In the user record a column profile_id, which points to the primary key of the profile, is automatically added for you.

    User

    User Record

    Profile

    Profile

    https://github.com/typeorm/typeorm/blob/2e671386f486dab908c54a9488334dc54f82c735/src/decorator/options/RelationOptions.ts#L16