← All @molecule/* packages · App templates
@molecule/api-notification-centerCore interface · notification-center · API (Node) · v1.0.1 · Apache-2.0
Notification center core interface for molecule.dev
npm install @molecule/api-notification-center@molecule/api-notification-center is the notification-center core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/api-notification-center-database.
import { setProvider, send, getAll, markRead } from '@molecule/api-notification-center'
import { createProvider } from '@molecule/api-notification-center-database'
// Startup — after the @molecule/api-database DataStore is bonded
setProvider(createProvider())
// Send an in-app notification
const notification = await send('user-123', {
type: 'system',
title: 'Welcome!',
body: 'Your account is ready.',
})
// List unread notifications
const { items, total } = await getAll('user-123', { read: false })
// Mark as read (scoped to the owner — only affects this user's row)
await markRead('user-123', notification.id)Providers (1): @molecule/api-notification-center-database
Works with: @molecule/api-bond, @molecule/api-i18n
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Notification center core interface for molecule.dev.
Per-user, persistent IN-APP notifications (the bell-icon inbox): send,
paginated listing, unread counts, mark-read, owner-scoped delete, and
per-user notification preferences. For fire-and-forget ops/broadcast
channels (Slack/webhook alerts), use @molecule/api-notifications instead.
import { setProvider, send, getAll, markRead } from '@molecule/api-notification-center'
import { createProvider } from '@molecule/api-notification-center-database'
// Startup — after the @molecule/api-database DataStore is bonded
setProvider(createProvider())
// Send an in-app notification
const notification = await send('user-123', {
type: 'system',
title: 'Welcome!',
body: 'Your account is ready.',
})
// List unread notifications
const { items, total } = await getAll('user-123', { read: false })
// Mark as read (scoped to the owner — only affects this user's row)
await markRead('user-123', notification.id)
core
npm install @molecule/api-notification-center @molecule/api-bond @molecule/api-i18n
BulkNotificationA bulk notification targeting a specific user.
interface BulkNotification {
/** The target user. */
userId: string
/** The notification to send. */
notification: CreateNotification
}
CreateNotificationData required to create a new notification.
interface CreateNotification {
/** Notification category. */
type: string
/** Notification headline. */
title: string
/** Notification body text. */
body: string
/** Arbitrary structured data to attach. */
data?: Record<string, unknown>
/** Delivery channels for this notification. Defaults to `['inApp']`. */
channels?: ('inApp' | 'email' | 'push' | 'sms')[]
}
NotificationAn in-app notification.
interface Notification {
/** Provider-assigned notification identifier. */
id: string
/** The user this notification belongs to. */
userId: string
/** Notification category (e.g. 'system', 'message', 'alert'). */
type: string
/** Notification headline. */
title: string
/** Notification body text. */
body: string
/** Whether the notification has been read. */
read: boolean
/** Arbitrary structured data attached to the notification. */
data?: Record<string, unknown>
/** When the notification was created. */
createdAt: Date
}
NotificationCenterProviderNotification center provider interface.
All notification center providers must implement this interface to provide in-app notification CRUD, read status management, and user preferences.
interface NotificationCenterProvider {
/**
* Sends a notification to a specific user.
*
* @param userId - The target user identifier.
* @param notification - The notification to create.
* @returns The created notification.
*/
send(userId: string, notification: CreateNotification): Promise<Notification>
/**
* Sends notifications to multiple users in a single batch.
*
* @param notifications - Array of user-targeted notifications.
* @returns The created notifications.
*/
sendBulk(notifications: BulkNotification[]): Promise<Notification[]>
/**
* Retrieves all notifications for a user with optional filtering.
*
* @param userId - The user to retrieve notifications for.
* @param options - Optional query filters and pagination.
* @returns Paginated notification results.
*/
getAll(userId: string, options?: NotificationQuery): Promise<PaginatedResult<Notification>>
/**
* Returns the count of unread notifications for a user.
*
* @param userId - The user to count unread notifications for.
* @returns The unread notification count.
*/
getUnreadCount(userId: string): Promise<number>
/**
* Marks a single notification as read, scoped to its owner.
*
* Implementations MUST only affect rows where `user_id = userId` so a user
* can never mark another user's notification read by id (IDOR).
*
* @param userId - The owner whose notification should be marked read.
* @param notificationId - The notification to mark as read.
* @returns `true` if a row owned by `userId` was updated, `false` otherwise.
*/
markRead(userId: string, notificationId: string): Promise<boolean>
/**
* Marks all notifications for a user as read.
*
* @param userId - The user whose notifications should be marked read.
*/
markAllRead(userId: string): Promise<void>
/**
* Deletes a notification, scoped to its owner.
*
* Implementations MUST only affect rows where `user_id = userId` so a user
* can never delete another user's notification by id (IDOR).
*
* @param userId - The owner whose notification should be deleted.
* @param notificationId - The notification to delete.
* @returns `true` if a row owned by `userId` was deleted, `false` otherwise.
*/
delete(userId: string, notificationId: string): Promise<boolean>
/**
* Retrieves notification preferences for a user.
*
* @param userId - The user to retrieve preferences for.
* @returns The user's notification preferences.
*/
getPreferences(userId: string): Promise<NotificationPreferences>
/**
* Updates notification preferences for a user.
*
* @param userId - The user to update preferences for.
* @param preferences - The preferences to merge.
*/
setPreferences(userId: string, preferences: Partial<NotificationPreferences>): Promise<void>
}
NotificationPreferencesUser notification preferences.
interface NotificationPreferences {
/** Whether email notifications are enabled. */
email: boolean
/** Whether push notifications are enabled. */
push: boolean
/** Whether SMS notifications are enabled. */
sms: boolean
/** Per-channel or per-type overrides. */
channels: Record<string, boolean>
}
NotificationQueryQuery options for listing notifications.
interface NotificationQuery {
/** Maximum number of results to return. */
limit?: number
/** Number of results to skip. */
offset?: number
/** Filter by read status. */
read?: boolean
/** Filter by notification type. */
type?: string
}
PaginatedResultPaginated result set.
interface PaginatedResult<T> {
/** The result items for this page. */
items: T[]
/** Total number of matching items. */
total: number
/** Number of items skipped. */
offset: number
/** Maximum items per page. */
limit: number
}
deleteNotification(userId, notificationId)Deletes a notification, scoped to its owner.
Only deletes the notification when it belongs to userId, preventing a user
from deleting another user's notification by id (IDOR).
function deleteNotification(userId: string, notificationId: string): Promise<boolean>
userId — The owner whose notification should be deleted.notificationId — The notification to delete.Returns: true if a row owned by userId was deleted, false otherwise.
getAll(userId, options)Retrieves all notifications for a user with optional filtering.
function getAll(userId: string, options?: NotificationQuery): Promise<PaginatedResult<Notification>>
userId — The user to retrieve notifications for.options — Optional query filters and pagination.Returns: Paginated notification results.
getPreferences(userId)Retrieves notification preferences for a user.
function getPreferences(userId: string): Promise<NotificationPreferences>
userId — The user to retrieve preferences for.Returns: The user's notification preferences.
getProvider()Retrieves the bonded notification center provider, throwing if none is configured.
function getProvider(): NotificationCenterProvider
Returns: The bonded notification center provider.
getUnreadCount(userId)Returns the count of unread notifications for a user.
function getUnreadCount(userId: string): Promise<number>
userId — The user to count unread notifications for.Returns: The unread notification count.
hasProvider()Checks whether a notification center provider is currently bonded.
function hasProvider(): boolean
Returns: true if a notification center provider is bonded.
markAllRead(userId)Marks all notifications for a user as read.
function markAllRead(userId: string): Promise<void>
userId — The user whose notifications should be marked read.Returns: Resolves when all notifications have been marked read.
markRead(userId, notificationId)Marks a single notification as read, scoped to its owner.
Only affects the notification when it belongs to userId, preventing a user
from marking another user's notification read by id (IDOR).
function markRead(userId: string, notificationId: string): Promise<boolean>
userId — The owner whose notification should be marked read.notificationId — The notification to mark as read.Returns: true if a row owned by userId was updated, false otherwise.
send(userId, notification)Sends a notification to a specific user.
function send(userId: string, notification: CreateNotification): Promise<Notification>
userId — The target user identifier.notification — The notification to create.Returns: The created notification.
sendBulk(notifications)Sends notifications to multiple users in a single batch.
function sendBulk(notifications: BulkNotification[]): Promise<Notification[]>
notifications — Array of user-targeted notifications.Returns: The created notifications.
setPreferences(userId, preferences)Updates notification preferences for a user.
function setPreferences(
userId: string,
preferences: Partial<NotificationPreferences>,
): Promise<void>
userId — The user to update preferences for.preferences — The preferences to merge.Returns: Resolves when preferences have been updated.
setProvider(provider)Registers a notification center provider as the active singleton. Called by bond packages during application startup.
function setProvider(provider: NotificationCenterProvider): void
provider — The notification center provider implementation to bond.| Provider | Package |
|---|---|
| Notifications | @molecule/api-notification-center-database |
Peer dependencies:
@molecule/api-bond ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-bond
@molecule/api-i18n
Wire the database first — and migrate the tables. The database-backed
bond (@molecule/api-notification-center-database) persists through the
bonded @molecule/api-database DataStore: bond the DataStore before
setProvider(createProvider()), and create the notifications and
notification_preferences tables in your app's migrations (see the bond's
docs for the expected columns) — nothing auto-creates them.
Every operation is owner-scoped by userId — markRead /
deleteNotification affect only that user's row (returning false when
nothing matched) and getAll returns only that user's items. In handlers,
ALWAYS pass the AUTHENTICATED user's id — forwarding a client-supplied
userId recreates the cross-user access the scoping exists to prevent.
getAll returns a paginated { items, total, offset, limit } result —
drive the UI from items + total, not items.length.
Sending here does NOT deliver email/push — it writes the in-app record.
Fan out to @molecule/api-emails / push packages separately (honoring
getPreferences(userId)) when a notification should also leave the app.
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual bell/feed and notification triggers, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
data. Sending here writes only the in-app
record — do not expect it to also arrive by email/push.getUnreadCount) increments when a new
notification arrives and equals the number of unread items shown in the
feed.markRead) flips that item to read and drops the
badge by one; mark-all-read (markAllRead) shows every item as read and
the badge as zero — and BOTH changes persist across a full reload (they
are stored, not client-only state).data) and, where that is the intended behavior, marks it read.deleteNotification) removes it from the feed
and it does NOT reappear on reload (it is deleted from the store, not just
hidden client-side).getAll returns just the
authenticated user's items, a notification created for user A never shows
for user B, and no route returns or mutates another user's notification by
id (markRead/deleteNotification on someone else's id must no-op and
return false, never touch that row). Handlers pass the AUTHENTICATED
user's id — never a client-supplied userId.