I agree that it would be nice if Prisma could more natively support this sort of self-relation where the relationship is expected to be symmetric (e.g. userA is friends with userB if and only if userB is friends with userA).
However, as far as I can tell Prisma insists on having two "sides" of the relationship. (If someone knows better, I would love to hear it!) So what follows is the approach I am taking, which avoids having to query both relations to find a user's full set of friends.
Concept
We'll use one "side" of the relation to contain the complete set of friends. The other "side" exists solely to meet Prisma's requirement, and we'll never query it directly.
When adding or removing a friend relationship, we'll make two prisma calls, one to update each object.
Code
Schema file:
model User {
id Int @id @default(autoincrement())
name String?
friends User[] @relation("UserFriends")
// This second "side" of the UserFriends relation exists solely
// to satisfy prisma's requirements; we won't access it directly.
symmetricFriends User[] @relation("UserFriends")
}
Methods to add and remove friendships (there's plenty of redundant code in here that could be abstracted out, but I think it's clearer to read this way):
const addFriendship = async (userIdA: string, userIdB: string) => {
await prisma.user.update({
where: {id: userIdA},
data: {friends: {connect: [{id: userIdB}]}},
});
await prisma.user.update({
where: {id: userIdB},
data: {friends: {connect: [{id: userIdA}]}},
});
};
const removeFriendship = async (userIdA: string, userIdB: string) => {
await prisma.user.update({
where: {id: userIdA},
data: {friends: {disconnect: [{id: userIdB}]}},
});
await prisma.user.update({
where: {id: userIdB},
data: {friends: {disconnect: [{id: userIdA}]}},
});
}
With this approach, one can load a user and get all their friends in the expected manner, e.g.
const getUserWithFriends = async (userId) =>
await prisma.user.find({
where: {id: userId},
include: {friends: true},
});
friends User[]
field, but you would lose any meta-information about the friendship. – HakimFriends User[]
many-to-many self-relation to know who actually is friends, which would, again, come with an additionalFriendsThrowaway User[]
field. I also don't think it's common to use a many-to-many self-relation field for more than one purpose, which is why it's unfortunate that Prisma requires two fields. Seems cleaner to do it the old way by creating a "Friends" table composed of two composite keys. – Connieconniption