Query returns a reference to a many to many record

I have a client query that should return notifications objects but the returned data is a reference to a many to many record:

  returnNotifications: async (root, args, {res, req}) => {
    const notifications = await prisma.user.findMany({
      where: {id: 1},
      select: {
        notifications: true,
      },
    });

    console.log(...notifications);
    return notifications;
  },

returns:

{ notifications: [ { userId: 1, notificationId: 7, watched: false } ] }

My Prisma schema:

model Notification {
  id                Int     @id @default(autoincrement())
  actor_id          Int
  link              String?
  movie_id          Int?
  message           String
  icon              String?
  thumbnail         String?
  users             NotificationsOnUsers[]
}

model User {
  id                Int     @id @default(autoincrement())
  email             String  @unique
  name              String
  user_name         String  @unique
  password          String
  movies            Movie[]
  notifications     NotificationsOnUsers[]
  followedBy        User[] @relation("UserFollows", references: [id])
  following         User[] @relation("UserFollows", references: [id])
}

model NotificationsOnUsers {
  user              User          @relation(fields: [userId], references: [id])
  userId            Int
  notification      Notification  @relation(fields: [notificationId], references: [id])
  notificationId    Int
  watched           Boolean

  @@id([userId, notificationId])
}

I would expect this to return all notifications connected to the user (1).

I think prisma calls that include, meaning “include” this object, whereas select means “select this field”, which is just the id in this case.

Instead of querying the user table I’ve changed the query to target the relation table between notifications and users:

  returnNotifications: async (root, args, {res, req}) => {
    const userNotifications = await prisma.notificationsOnUsers.findMany({
      where: {userId: 1},
      include: {notification: true},
    });
    console.log(userNotifications);

    return userNotifications
  },

Which returns an array:

[
  {
    userId: 1,
    notificationId: 7,
    watched: false,
    notification: {
      id: 7,
      actor_id: 1,
      link: null,
      movie_id: 438631,
      message: 'alucardu has added Dune to their watchlist.',
      icon: null,
      thumbnail: null
    }
  }
]

The return value is the desired result, so I’m happy in that regard. The idea is that there’s a single notification which is shared between a lot of users, so the watched property (shows the notification in grey if it has been seen) cannot go into the notification object itself, since it will be different for each user. So I put it on the field of the relationship object. Is this approach okay? Thanks for pointing out include :slight_smile:

Perhaps; I can’t say what your model is but in most systems I would argue that a notification is a one-to-many. One user, many notifications.

A “notification” that alerts many users sounds more like an event which should generate notifications.

For example, let’s take youtube. You have a video, and you have mutations on it, such as “release”. When a video is released, that mutation creates a “release” event, which then generates a notification for each user that has subscribed to notifications (via the notification bell).

Thus, one event, many notifications, each with its own discrete status, which then allows you to delete the notifications on a per-user basis (as one user shouldn’t be able to delete a notification for all other users).

type User {
  notifications: [Notification]
}

type Notification {
  user: User
  event: Event
  message: String
}

type Event {
  type: EVENT_TYPE
  notifications: [Notification]
  createdAt: DateTime
}

type Video {
  channel: Channel
  ...
}

type Channel {
  subscribers: [User]
}

extend type Mutation {
  releaseVideo(id: ID!): VideoReleaseResult # generates an event of type VIDEO_RELEASED, and passes in the channel name for purposes of constructing the notification message
}

type VideoReseaseResult {
  success: Boolean!
  event: Event!
}

Thanks @kevin-lindsay-1 for the insights. The way I have it setup is that a user will only change the watched state of their user-notification relation record. So they will not interact with the actual notification that’s shared between users.

situation A (current setup):

Sally has 100 followers. She reviews a movie. 1 notification record is created in the _Notification table. 100 records are created in the _NotificationToUser table and each user has a reference added to their _User table.

situation B (proposed setup):

Sally has a 100 followers. She reviews a movie. 1 notification record is created in each of her followers _User table.

Looks like situation B is better since it won’t create a endless amount of relation and allows to query notifications directly on the user so there’s no need to look through the notifications relationship table.

Thanks!

1 Like