Notifications
Notification types
Reference for buildNotificationType in VitNode. Every option, the arguments of present, how output is cleaned, grouping, schema versions and mandatory types.
A notification type describes one kind of notification: the data it stores, how it reads in the inbox, who may see it and what members get by default. You build it with buildNotificationType() and register it on your API plugin. For a guided first type, see Publish notifications.
Signature
import { buildNotificationType } from '@vitnode/core/api/lib/notifications/registry'
buildNotificationType<TData>(
definition: NotificationTypeDefinition<TData>,
): NotificationTypeDefinition<TData>It returns the definition unchanged after checking it. Pass that same object to notificationTypes and to publish({ type }), so TypeScript checks data against schema at every call site.
Register types on buildApiPlugin({ notificationTypes }), or on any module with buildModule({ notificationTypes }). Core collects them from every installed plugin when the API starts.
Options
Prop
Type
buildNotificationType() throws a NotificationRegistryError when the module loads if the id, category or subject type is invalid, version is below 1, grouping.windowMinutes is outside 1 to 10,080 (one week), or defaults.email is not "none" while email is missing. Two registered types with the same id stop the API from starting.
present arguments
present runs on the API each time an item is listed or emailed. It receives:
Prop
Type
A missing label translation shows the type id instead. Category names are looked up as core.notifications.categories.<category>, then <pluginId>.notifications.categories.<category>.
How core cleans present output
Core cleans what present returns before anyone sees it:
titleandbodyare plain text. Markup is never rendered, whitespace is collapsed and each string is cut at 500 characters.targetmust be a path on the same site, such as/forum/topics/42. Absolute URLs,//host,javascript:and backslashes are dropped, and the item renders without a link.- If
presentthrows or returns an empty title, the item shows "This notification is no longer available." and the error is logged.
access
Core calls access once per delivery batch, again before each email or digest, and each time a member's inbox loads. It receives a bounded batch of user ids, never the whole audience. A worked example is in Limit who sees a notification.
grouping
grouping: {
windowMinutes: 60,
key: ({ data }) => `topic:${data.topicId}`,
},Notifications with the same type and key, for the same member, in the same window join one inbox item, such as "Alex and 4 others replied to Shipping day". Windows are fixed UTC slots: with 60 minutes, 14:00 to 14:59 is one window. The key defaults to the subject. A notification without a subject, or a key that returns null, is never grouped. Use actors, actorCount and eventCount in present to word a grouped item:
"title": "{others, plural, =0 {{name} replied} one {{name} and # other replied} other {{name} and # others replied}} to {title}"version and migrate
Stored notifications outlive your code. When schema changes shape, increase version and make migrate upgrade the old data. Here version 1 stored title, and version 2 renames it to topicTitle:
export const topicReplyNotification = buildNotificationType({
id: 'forum.topic_reply',
version: 2,
schema: z.object({ topicId: z.number(), topicTitle: z.string() }),
migrate: (data, fromVersion) => {
if (fromVersion !== 1) return data
const old = data as { title: string; topicId: number }
return { topicId: old.topicId, topicTitle: old.title }
},
// other options unchanged
})Data that cannot be upgraded is never shown. That covers a missing migrate, a stored version newer than the code, a thrown error or a failed parse. A notification not yet delivered is marked failed, and inbox items show the "no longer available" placeholder.
mandatory
Set mandatory: true for account notices members must not miss, such as "Your role changed". The type always reaches the inbox, ignores AdminCP policies that would disable it, and shows as locked on the preferences page. Its email follows the installation default.
Password resets, email verification and sign-in codes are not notifications. Keep sending them with c.get("email").send(), so they arrive even for members who turned every notification off.