Notifications GraphQL API
Real-time notification system with queries, mutations, and WebSocket subscriptions.
Notification Targeting
Notifications support three targeting modes:
| Mode | Condition | Audience |
|---|---|---|
| Direct | targetUserId is set | Single user |
| Role-based | targetUserId is null, targetRoles is set | All users whose groups grant any of the specified roles |
| Broadcast | Both targetUserId and targetRoles are null | All active users in the organization |
targetUserId and targetRoles are mutually exclusive — setting both is a validation error. targetRoles must not be an empty list.
Read Status
Read status (isRead, readAt) is tracked per user via a UserNotification junction table. UserNotification rows are created lazily — only when a user marks a notification as read. Until then, the notification appears as unread.
Entity Links in the UI
When a notification includes both entityType and entityId, the web UI attempts to open the matching app route as a dialog. It builds a path in the form entityType/entityId, checks the matched route's permission, permissions, or requiredPermissions, and passes matched route parameters plus organizationId and currentUser into the dialog component. If no route exists or the user lacks permission, the UI shows an error instead of opening the item.
Queries
getNotification
Fetch a single notification for the current user.
query {
getNotification(organizationId: 1, notificationId: 42) {
notificationId
title
message
type
priority
targetUserId
targetRoles
entityType
entityId
isRead
readAt
created
}
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
organizationId | Int | Yes | Organization scope |
notificationId | Int | Yes | Notification to fetch |
Returns null if the notification doesn't exist or isn't visible to the current user (see Notification Targeting).
getNotifications
Paginated list of notifications for the current user. Supports filtering, searching, and sorting.
query {
getNotifications(organizationId: 1, filter: "isRead:false", orderBy: "-created", skip: 0, take: 20) {
items {
notificationId
title
message
type
priority
targetRoles
isRead
created
}
totalCount
}
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
organizationId | Int | Yes | Organization scope |
filter | String | No | Lucene filter syntax (e.g. isRead:false, type:Alert) |
search | String | No | Full-text search |
orderBy | String | No | Sort field (default: -created = newest first) |
getUnreadNotificationCount
Returns the count of unread notifications for the current user.
query {
getUnreadNotificationCount(organizationId: 1)
}
Mutations
createNotification
Create a new notification. Targeting is determined by targetUserId and targetRoles — see Notification Targeting.
mutation {
createNotification(organizationId: 1, values: {
title: "Order #1234 shipped"
message: "Your order has been picked up by the carrier."
type: ORDER_UPDATE
priority: NORMAL
entityType: "Order"
entityId: 1234
}) {
notificationId
title
created
}
}
Input fields (CreateNotificationCommandValues):
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
title | String | Yes | — | Notification title |
message | String | No | — | Notification body (supports Markdown) |
type | NotificationType | Yes | — | System, OrderUpdate, TaskAssignment, Alert, Info |
priority | NotificationPriority | No | Normal | Low, Normal, High, Urgent |
targetUserId | String | No | — | Target a specific user (mutually exclusive with targetRoles) |
targetRoles | [String] | No | — | Target users by role names (mutually exclusive with targetUserId; must not be empty if provided) |
entityType | String | No | — | Linked entity type name |
entityId | Int | No | — | Linked entity ID |
expiresAt | DateTime | No | — | Optional expiration |
markNotificationRead
Mark a single notification as read for the current user.
mutation {
markNotificationRead(organizationId: 1, notificationId: 42)
}
Returns true on success.
markAllNotificationsRead
Mark all notifications as read for the current user. Returns the count of notifications marked.
mutation {
markAllNotificationsRead(organizationId: 1)
}
deleteNotification
Delete a notification (removes for all users).
mutation {
deleteNotification(organizationId: 1, notificationId: 42) {
deletedCount
id
}
}
Subscriptions
onNotificationReceived
Real-time WebSocket subscription for new notifications. Uses PostgreSQL NOTIFY/LISTEN under the hood.
subscription {
onNotificationReceived(organizationId: 1, userId: "user-abc-123") {
notificationId
title
message
type
priority
entityType
entityId
isRead
created
createdBy
}
}
Topic format: {organizationId}_{userId}_notifications
The subscription fires whenever a new notification is created that targets the specified user (either directly or via broadcast).
Frontend Integration
The frontend uses the useNotifications React hook which:
- Fetches notifications via
GetNotificationsquery (RTK Query) - Fetches unread count via
GetUnreadNotificationCountquery - Subscribes to
onNotificationReceivedviagraphql-wsWebSocket client - Shows toast notifications for real-time updates
- Provides
markAsRead,markAllAsRead, anddeleteNotificationactions
The NotificationsDropdown component in the navbar renders the notification bell icon with unread badge and a dropdown panel with the notification list. Message content is rendered as Markdown.