I have made a follower system using typeorm/mysql
and nodejs/express
.
Save logic:
router.post("/subscriptions", async (req, res) => {
const currentUserId = Number(req.headers.userid);
const userToFollowId = req.body.userToFollowId;
const userRepository = getRepository(User);
try {
let user;
let userToFollow;
user = await userRepository.findOneOrFail(currentUserId);
userToFollow = await userRepository.findOneOrFail(userToFollowId);
if (user && userToFollow) {
user.followers = [...(user?.followers || []), userToFollow];
await userRepository.save(user);
res.status(201).send("Followed");
}
} catch (e) {
console.log(e);
}
});
@User
Entity which has self referencing relation:
@Entity()
export class User {
@PrimaryGeneratedColumn()
id!: number;
@Column({ nullable: true })
name!: string;
@Column()
email!: string;
@Column()
password!: string;
@OneToMany(() => Post, (post) => post.user)
posts!: Post[];
@OneToMany(() => Comment, (comment) => comment.user)
comments!: Comment[];
@ManyToMany(() => User, (user) => user.following, { cascade: true })
@JoinTable()
followers!: User[];
@ManyToMany(() => User, (user) => user.followers)
following!: User[];
@Column({
default: () => "NOW()",
})
createdAt!: Date;
@Column({
default: () => "NOW()",
})
@UpdateDateColumn()
updatedAt!: Date;
}
Let's say curentUserId
is 5 and userToFollow
ID is 6. It will be saved to the database successfully. The problem occurs when I try to save another entry by using same currentUserId
of 5 and userToFollow
some other ID (for example 7), in this case previous entry in database:
User 5
follows user with the ID of 6
will be overwritten
to:
User 5
follows user with the ID of 7
Here is the query when I save it for the first time:
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: [5]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: ["6"]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?, ?) -- PARAMETERS: [5,6]
query: SELECT `User_followers_rid`.`userId_1` AS `userId_1`, `User_followers_rid`.`userId_2` AS `userId_2` FROM `user` `user` INNER JOIN `user_followers_user` `User_followers_rid` ON (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`) OR (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`) ORDER BY `User_followers_rid`.`userId_2` ASC, `User_followers_rid`.`userId_1` ASC -- PARAMETERS: [5,6]
query: START TRANSACTION
query: INSERT INTO `user_followers_user`(`userId_1`, `userId_2`) VALUES (?, ?) -- PARAMETERS: [5,6]
query: COMMIT
And this is second one where I think the problem occurs (please note the DELETE
part):
query: START TRANSACTION
query: INSERT INTO `user_followers_user`(`userId_1`, `userId_2`) VALUES (?, ?) -- PARAMETERS: [5,6]
query: COMMIT
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: [5]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: ["7"]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?, ?) -- PARAMETERS: [5,7]
query: SELECT `User_followers_rid`.`userId_1` AS `userId_1`, `User_followers_rid`.`userId_2` AS `userId_2` FROM `user` `user` INNER JOIN `user_followers_user` `User_followers_rid` ON (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`) OR (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`) ORDER BY `User_followers_rid`.`userId_2` ASC, `User_followers_rid`.`userId_1` ASC -- PARAMETERS: [5,7]
query: START TRANSACTION
query: INSERT INTO `user_followers_user`(`userId_1`, `userId_2`) VALUES (?, ?) -- PARAMETERS: [5,7]
query: DELETE FROM `user_followers_user` WHERE `userId_1` = ? AND `userId_2` = ? -- PARAMETERS: [5,6]
query: COMMIT
The reason you have this problem is that when you query user, you are not adding followers
relation with it. Write a console.log(user)
and you will see user
object doesn't have followers
attribute. That's why every time you call the api, it replaces the old one with new one.
You have to add followers
relation in your query. This will return all the existing follower's list in form of user.followers
of User
object. Another thing you can use
user.followers.push(userToFollow);
instead of user.followers = [...(user?.followers || []), userToFollow];
in your code. Use the code below,
try {
let user = await userRepository.findOneOrFail(currentUserId, { relations: ["followers"] });
let userToFollow = await userRepository.findOneOrFail(userToFollowId);
if (user && userToFollow) {
user.followers.push(userToFollow);
await userRepository.save(user);
return res.status(201).send("Followed");
} else {
throw new Error("User not found!")
}
} catch (e) {
console.log(e);
}