Skip to main content
SlugShare’s notification system keeps users informed when their requests are accepted or declined, and when they accept requests from others.

Notification Model

Notifications are stored in the database with the following schema:
prisma/schema.prisma
model Notification {
  id        String   @id @default(cuid())
  userId    String
  type      String   // "request_accepted", "request_declined", etc.
  message   String
  read      Boolean  @default(false)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
Notification Types:
  • request_accepted - Your request was accepted by a donor
  • request_declined - Your request was declined by a potential donor
  • request_accepted_by_you - Confirmation that you accepted someone’s request

Automatic Notification Creation

Notifications are automatically created when requests are accepted or declined:

When a Request is Accepted

Two notifications are created in the same database transaction as the points transfer:
app/api/requests/[id]/accept/route.ts
await prisma.$transaction([
  // ... points transfer operations ...

  // Notification for requester
  prisma.notification.create({
    data: {
      userId: request.requesterId,
      type: "request_accepted",
      message: `${user.name || user.email} accepted your request for ${request.pointsRequested} points at ${request.location}`,
      read: false,
    },
  }),

  // Confirmation notification for donor
  prisma.notification.create({
    data: {
      userId: user.id,
      type: "request_accepted_by_you",
      message: `You accepted ${request.requester.name || request.requester.email}'s request for ${request.pointsRequested} points at ${request.location}`,
      read: false,
    },
  }),
]);
Creating notifications within the transaction ensures they are only created if the points transfer succeeds. If the transaction fails, no notifications are created.

When a Request is Declined

A notification is sent to the requester:
await prisma.notification.create({
  data: {
    userId: request.requesterId,
    type: "request_declined",
    message: `${user.name || user.email} declined your request for ${request.pointsRequested} points at ${request.location}`,
    read: false,
  },
});

Fetching Notifications

Users can retrieve all their notifications via the API:
1

Send GET request

const response = await fetch("/api/notifications");
const notifications = await response.json();
2

API retrieves notifications

The API fetches all notifications for the authenticated user, ordered by newest first:
app/api/notifications/route.ts
export async function GET() {
  const user = await getCurrentUser();

  if (!user || !user.id) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const notifications = await prisma.notification.findMany({
    where: { userId: user.id },
    orderBy: { createdAt: "desc" },
  });

  return NextResponse.json(notifications);
}
3

Display notifications

Notifications can be displayed in a list showing:
  • Notification message
  • Read/unread status
  • Timestamp
  • Notification type

Marking Notifications as Read

Users can mark notifications as read to track which updates they’ve seen:
1

Send PATCH request

const response = await fetch("/api/notifications", {
  method: "PATCH",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    notificationId: "clx123abc",
    read: true,
  }),
});
2

Validate notification ownership

The API ensures users can only update their own notifications:
app/api/notifications/route.ts
export async function PATCH(request: NextRequest) {
  const user = await getCurrentUser();
  if (!user || !user.id) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const { notificationId, read } = await request.json();

  if (typeof notificationId !== "string") {
    return NextResponse.json(
      { error: "Notification ID is required" },
      { status: 400 }
    );
  }

  const notification = await prisma.notification.update({
    where: {
      id: notificationId,
      userId: user.id, // Ensure user can only update their own notifications
    },
    data: {
      read: read === true,
    },
  });

  return NextResponse.json(notification);
}
Users can only mark their own notifications as read. Attempting to update another user’s notification will fail.

Notification Message Format

Notification messages are dynamically generated with relevant details:

Request Accepted

`${donorName} accepted your request for ${pointsAmount} points at ${location}`
Example: “John Smith accepted your request for 15 points at Cowell/Stevenson”

Request Declined

`${donorName} declined your request for ${pointsAmount} points at ${location}`
Example: “Jane Doe declined your request for 20 points at Crown/Merrill”

You Accepted a Request

`You accepted ${requesterName}'s request for ${pointsAmount} points at ${location}`
Example: “You accepted Alice Johnson’s request for 10 points at Porter/Kresge”

Notification Badge Count

To display an unread notification count badge:
const unreadCount = notifications.filter((n) => !n.read).length;
This can be shown in the navigation bar or notification icon to alert users to new notifications.

Best Practices

1

Poll for notifications periodically

Since SlugShare doesn’t use WebSockets, implement polling to check for new notifications:
useEffect(() => {
  const interval = setInterval(async () => {
    const response = await fetch("/api/notifications");
    const notifications = await response.json();
    setNotifications(notifications);
  }, 30000); // Poll every 30 seconds

  return () => clearInterval(interval);
}, []);
2

Mark as read on view

When a user opens the notifications panel, automatically mark all notifications as read:
const markAllAsRead = async () => {
  for (const notification of unreadNotifications) {
    await fetch("/api/notifications", {
      method: "PATCH",
      body: JSON.stringify({
        notificationId: notification.id,
        read: true,
      }),
    });
  }
};
3

Show most recent first

Notifications are already ordered by createdAt: "desc" from the API, so the most recent notifications appear first in the list.

Viewing Notifications in the Inbox

SlugShare provides a dedicated inbox page at /inbox where users can view and manage all their notifications.

Inbox Features

1

View all notifications

The inbox displays all notifications ordered by most recent first, with unread notifications highlighted with a visual indicator.
2

Unread count badge

The inbox header shows the number of unread notifications:
const unreadCount = notifications.filter((n) => !n.read).length;
3

Mark as read

Each unread notification has a “Mark as Read” button that calls the PATCH endpoint:
app/inbox/page.tsx
const markAsRead = async (notificationId: string) => {
  const response = await fetch("/api/notifications", {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ notificationId, read: true }),
  });
  
  // Update local state
  setNotifications((prev) =>
    prev.map((notif) =>
      notif.id === notificationId ? { ...notif, read: true } : notif
    )
  );
};
4

Mark all as read

The inbox provides a “Mark All as Read” button that marks all unread notifications as read in one action.
5

Refresh notifications

Users can manually refresh the notification list to check for new updates.

Accessing the Inbox

Users can access the inbox from the dashboard navigation. The inbox page fetches all notifications on load and displays them in a card-based layout.
app/inbox/page.tsx
useEffect(() => {
  fetchNotifications();
}, []);

const fetchNotifications = async () => {
  const response = await fetch("/api/notifications");
  const data = await response.json();
  setNotifications(data);
};
The inbox page uses client-side polling to check for new notifications every 30 seconds, keeping users informed of new request updates without requiring a page refresh.

Future Enhancements

Potential improvements to the notification system:
  • Email notifications - Send emails for important events
  • Push notifications - Use web push API for real-time updates
  • Notification preferences - Allow users to control which notifications they receive
  • Notification grouping - Group similar notifications together
  • Delete notifications - Allow users to dismiss notifications permanently

Build docs developers (and LLMs) love