Ellie G
Ellie G

Reputation: 259

TypeORM - Create user with relations

When a new user signs up, I want to create a number of relations to be automatically be set up as well. Currently, I am doing it this way:

const user = await User.create({
  email,
  password: hashedPassword,
}).save();

const settings = await Settings.create({
  userId: user.id,
}).save();

const settings = await Profile.create({
  userId: user.id,
}).save();

From what I've read, transactions are the best way to handle dependant operations, but PG doesn't create user ID until it is saved.

This is the best I could come up with transactions:

const user = await User.create({
  email,
  password: hashedPassword,
}).save();

const settings = await Settings.create({
  userId: user.id,
});

const profile = await Profile.create({
  userId: user.id,
});

await getConnection().transaction(async (transactionalEntityManager) => {
  await transactionalEntityManager.save(settings);
  await transactionalEntityManager.save(profile);
});

However, there is still the possibility that the user saves, but the relations do not.

Is there a better (or easier way) to handle this?

user.entity.ts

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id!: number;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

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

  @Column()
  password!: string;

  @OneToOne(() => Settings, (settings) => settings.user)
  settings: Settings;

  @OneToOne(() => Profile, (profile) => profile.user)
  profile: Profile;
}

settings.entity.ts

@Entity()
export class Settings {
  @PrimaryColumn()
  userId: number;

  @OneToOne(() => User, (user) => user.settings)
  user: User;
}

profile.entity.ts

@Entity()
export class Profile {
  @PrimaryColumn()
  userId: number;

  @OneToOne(() => User, (user) => user.profile)
  user: User;
}

Upvotes: 1

Views: 6123

Answers (1)

noam steiner
noam steiner

Reputation: 4444

The save method loads the relations before INSERT, therefore it is not possible to perform save for settings, The generated query will first try to SELECT the user and only then INSERT settings, as you said - inside a transaction it is not possible to select uncommitted records, and that why it doesn't work.

What can be done? Two options:

  1. Save the entities nested
const user = await getConnection()
.transaction((transactionalEntityManager) => {
   const userObj = User.create({
      email,
      password: hashedPassword,
   })

   userObj.profile = Profile.create({});
   userObj.settings = Settings.create({});

   return transactionalEntityManager.save(User, userObj);
});
  1. Insert the user, and then insert the settings and profile
const userInsertResult = await getConnection()
.transaction(async (transactionalEntityManager) => {

const userInsertResult = await transactionalEntityManager
                .createQueryBuilder(User,'user')
                .insert()
                .into(User)
                .values({
                   email,
                   password: hashedPassword
                }).execute();

const settingsInsertResult = await transactionalEntityManager
                .createQueryBuilder(Settings,'settings')
                .insert()
                .into(Settings)
                .values({
                   userId: userInsertResult.raw.id
                }).execute();

const profileInsertResult = await transactionalEntityManager
                .createQueryBuilder(Profile,'profile')
                .insert()
                .into(Profile)
                .values({
                   userId: userInsertResult.raw.id
                }).execute();

 return userInsertResult
});

Upvotes: 1

Related Questions